diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 5ef23759b3e18e..29831604b79f7b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -158,8 +158,7 @@ /doc/contributing/maintaining/maintaining-single-executable-application-support.md @nodejs/single-executable /src/node_sea* @nodejs/single-executable /test/fixtures/postject-copy @nodejs/single-executable -/test/parallel/test-single-executable-* @nodejs/single-executable -/test/sequential/test-single-executable-* @nodejs/single-executable +/test/sea @nodejs/single-executable /tools/dep_updaters/update-postject.sh @nodejs/single-executable # Permission Model @@ -229,3 +228,6 @@ /lib/path.js @nodejs/path /lib/path/* @nodejs/path /test/parallel/test-path-* @nodejs/path + +# userland-migrations +/doc/api/deprecations.md @nodejs/userland-migrations diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml index 4f987182cd8dcf..f0404da2b804e8 100644 --- a/.github/workflows/linters.yml +++ b/.github/workflows/linters.yml @@ -140,6 +140,22 @@ jobs: NODE=$(command -v node) make lint-md env: NODE_RELEASED_VERSIONS: ${{ steps.get-released-versions.outputs.NODE_RELEASED_VERSIONS }} + lint-nix: + if: github.event.pull_request.draft == false + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + persist-credentials: false + sparse-checkout: '*.nix' + sparse-checkout-cone-mode: false + - uses: cachix/install-nix-action@7be5dee1421f63d07e71ce6e0a9f8a4b07c2a487 # v31.6.1 + - name: Lint Nix files + run: | + nix-shell -I nixpkgs=./tools/nix/pkgs.nix -p 'nixfmt-tree' --run ' + treefmt --quiet --fail-on-change + ' || git --no-pager diff --exit-code + lint-py: if: github.event.pull_request.draft == false runs-on: ubuntu-latest diff --git a/.github/workflows/test-linux.yml b/.github/workflows/test-linux.yml index cf2b0e1a0833b2..53329ca37c126d 100644 --- a/.github/workflows/test-linux.yml +++ b/.github/workflows/test-linux.yml @@ -5,6 +5,7 @@ on: paths-ignore: - .mailmap - README.md + - '*.nix' - .github/** - '!.github/workflows/test-linux.yml' types: [opened, synchronize, reopened, ready_for_review] @@ -17,6 +18,7 @@ on: paths-ignore: - .mailmap - README.md + - '*.nix' - .github/** - '!.github/workflows/test-linux.yml' diff --git a/.github/workflows/test-macos.yml b/.github/workflows/test-macos.yml index 80f0cba37ff16d..157994c1a022f6 100644 --- a/.github/workflows/test-macos.yml +++ b/.github/workflows/test-macos.yml @@ -6,6 +6,7 @@ on: paths-ignore: - .mailmap - '**.md' + - '*.nix' - AUTHORS - doc/** - .github/** @@ -19,6 +20,7 @@ on: paths-ignore: - .mailmap - '**.md' + - '*.nix' - AUTHORS - doc/** - .github/** diff --git a/.github/workflows/test-shared.yml b/.github/workflows/test-shared.yml new file mode 100644 index 00000000000000..a1ab5bba2f7edc --- /dev/null +++ b/.github/workflows/test-shared.yml @@ -0,0 +1,127 @@ +name: Test Shared libraries + +on: + pull_request: + paths-ignore: + - .mailmap + - '**.md' + - AUTHORS + - doc/** + - .github/** + - '!.github/workflows/test-shared.yml' + types: [opened, synchronize, reopened, ready_for_review] + push: + branches: + - main + - canary + - v[0-9]+.x-staging + - v[0-9]+.x + paths-ignore: + - .mailmap + - '**.md' + - AUTHORS + - doc/** + - .github/** + - '!.github/workflows/test-shared.yml' + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +env: + FLAKY_TESTS: keep_retrying + +permissions: + contents: read + +jobs: + build-tarball: + if: github.event.pull_request.draft == false + name: ${{ github.event_name == 'workflow_dispatch' && 'Skipped job' || 'Build slim tarball' }} + runs-on: ubuntu-24.04-arm + steps: + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + if: ${{ github.event_name != 'workflow_dispatch' }} + with: + persist-credentials: false + + - name: Make tarball + if: ${{ github.event_name != 'workflow_dispatch' }} + run: | + export DATESTRING=$(date "+%Y-%m-%d") + export COMMIT=$(git rev-parse --short=10 "$GITHUB_SHA") + ./configure && make tar -j4 SKIP_XZ=1 SKIP_SHARED_DEPS=1 + env: + DISTTYPE: nightly + + - name: Upload tarball artifact + if: ${{ github.event_name != 'workflow_dispatch' }} + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: tarballs + path: '*.tar.gz' + compression-level: 0 + + build: + needs: build-tarball + strategy: + fail-fast: false + matrix: + include: + - runner: ubuntu-24.04 + system: x86_64-linux + - runner: ubuntu-24.04-arm + system: aarch64-linux + - runner: macos-13 + system: x86_64-darwin + - runner: macos-latest + system: aarch64-darwin + name: '${{ matrix.system }}: with shared libraries' + runs-on: ${{ matrix.runner }} + steps: + - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 + if: ${{ github.event_name != 'workflow_dispatch' }} + with: + name: tarballs + path: tarballs + + - name: Extract tarball + if: ${{ github.event_name != 'workflow_dispatch' }} + run: | + tar xzf tarballs/*.tar.gz -C "$RUNNER_TEMP" + echo "TAR_DIR=$RUNNER_TEMP/$(basename tarballs/*.tar.gz .tar.gz)" >> "$GITHUB_ENV" + + - uses: cachix/install-nix-action@7be5dee1421f63d07e71ce6e0a9f8a4b07c2a487 # v31.6.1 + with: + extra_nix_config: sandbox = true + + - name: Configure sccache + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + with: + script: | + core.exportVariable('SCCACHE_GHA_VERSION', 'on'); + core.exportVariable('ACTIONS_CACHE_SERVICE_V2', 'on'); + core.exportVariable('ACTIONS_RESULTS_URL', process.env.ACTIONS_RESULTS_URL || ''); + core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || ''); + + - name: Load shell.nix + if: github.event_name != 'workflow_dispatch' + run: | + mv "$TAR_DIR"/*.nix . + mkdir tools + mv "$TAR_DIR"/tools/nix tools/. + + - name: Build Node.js and run tests + run: | + nix-shell \ + -I nixpkgs=./tools/nix/pkgs.nix \ + --pure --keep TAR_DIR --keep FLAKY_TESTS \ + --keep SCCACHE_GHA_VERSION --keep ACTIONS_CACHE_SERVICE_V2 --keep ACTIONS_RESULTS_URL --keep ACTIONS_RUNTIME_TOKEN \ + --arg loadJSBuiltinsDynamically false \ + --arg ccache '(import {}).sccache' \ + --arg devTools '[]' \ + --arg benchmarkTools '[]' \ + ${{ endsWith(matrix.system, '-darwin') && '--arg extraConfigFlags ''["--without-inspector"]'' \' || '\' }} + --run ' + make -C "$TAR_DIR" run-ci -j4 V=1 TEST_CI_ARGS="-p actions --measure-flakiness 9 --skip-tests=$CI_SKIP_TESTS" + ' diff --git a/.github/workflows/tools.yml b/.github/workflows/tools.yml index c101c6432fa899..3a6987837eec74 100644 --- a/.github/workflows/tools.yml +++ b/.github/workflows/tools.yml @@ -25,10 +25,12 @@ on: - gyp-next - histogram - icu + - inspector_protocol - libuv - llhttp - minimatch - nbytes + - nixpkgs-unstable - nghttp2 - nghttp3 - ngtcp2 @@ -149,6 +151,14 @@ jobs: cat temp-output tail -n1 temp-output | grep "NEW_VERSION=" >> "$GITHUB_ENV" || true rm temp-output + - id: inspector_protocol + subsystem: deps + label: dependencies, inspector + run: | + ./tools/dep_updaters/update-inspector-protocol.sh > temp-output + cat temp-output + tail -n1 temp-output | grep "NEW_VERSION=" >> "$GITHUB_ENV" || true + rm temp-output - id: libuv subsystem: deps label: dependencies @@ -181,6 +191,14 @@ jobs: cat temp-output tail -n1 temp-output | grep "NEW_VERSION=" >> "$GITHUB_ENV" || true rm temp-output + - id: nixpkgs-unstable + subsystem: tools + label: tools + run: | + ./tools/dep_updaters/update-nixpkgs-pin.sh > temp-output + cat temp-output + tail -n1 temp-output | grep "NEW_VERSION=" >> "$GITHUB_ENV" || true + rm temp-output - id: nghttp2 subsystem: deps label: dependencies @@ -276,11 +294,16 @@ jobs: with: persist-credentials: false - name: Set up Python ${{ env.PYTHON_VERSION }} - if: matrix.id == 'icu' && (github.event_name == 'schedule' || inputs.id == 'all' || inputs.id == matrix.id) + if: | + (matrix.id == 'icu' || matrix.id == 'inspector_protocol') && + (github.event_name == 'schedule' || inputs.id == 'all' || inputs.id == matrix.id) uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 with: python-version: ${{ env.PYTHON_VERSION }} allow-prereleases: true + - name: Set up Nix + if: matrix.id == 'nixpkgs-unstable' && (github.event_name == 'schedule' || inputs.id == 'all' || inputs.id == matrix.id) + uses: cachix/install-nix-action@7be5dee1421f63d07e71ce6e0a9f8a4b07c2a487 # v31.6.1 - run: ${{ matrix.run }} if: github.event_name == 'schedule' || inputs.id == 'all' || inputs.id == matrix.id env: diff --git a/BUILDING.md b/BUILDING.md index 84182f8fa05793..292b71a3fb8cf9 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -239,6 +239,7 @@ Consult previous versions of this document for older versions of Node.js: Installation via Linux package manager can be achieved with: +* Nix, NixOS: `nix-shell` * Ubuntu, Debian: `sudo apt-get install python3 g++-12 gcc-12 make python3-pip` * Fedora: `sudo dnf install python3 gcc-c++ make python3-pip` * CentOS and RHEL: `sudo yum install python3 gcc-c++ make python3-pip` @@ -259,6 +260,75 @@ installed, you can find them under the menu `Xcode -> Open Developer Tool -> More Developer Tools...`. This step will install `clang`, `clang++`, and `make`. +#### Nix integration + +If you are using Nix and direnv, you can use the following to get started: + +```bash +echo 'use_nix --arg sharedLibDeps {} --argstr icu small' > .envrc +direnv allow . +make build-ci -j12 +``` + +The use of `make build-ci` is to ensure you are using the `CONFIG_FLAGS` +environment variable. You can also specify it manually: + +```bash +./configure $CONFIG_FLAGS +make -j12 +``` + +Passing the `--arg sharedLibDeps {}` instructs direnv and Nix to generate an +environment that uses the vendored-in native dependencies. Using the vendored-in +dependencies result in a result closer to the official binaries, the tradeoff +being the build will take longer to complete as you'd have to build those +dependencies instead of using the cached ones from the Nix cache. You can omit +that flag to use all the shared dependencies, or specify only some dependencies: + +```bash +cat -> .envrc <<'EOF' +use nix --arg sharedLibDeps '{ + inherit (import {}) + openssl + zlib + ; +}' +EOF +``` + +Passing the `--argstr icu small` instructs direnv and Nix to pass `--with-intl=small` in +the `CONFIG_FLAGS` environment variable. If you omit this, the prebuilt ICU from Nix cache +will be used, which should speed up greatly compilation time. + +The use of `direnv` is completely optional, you can also use `nix-shell` directly, +e.g. here's a command you can use to build a binary for benchmarking purposes: + +```bash +# Passing `--arg loadJSBuiltinsDynamically false` to instruct the compiler to +# embed the JS core files so it is no longer affected by local changes +# (necessary for getting useful benchmark results). +# Passing `--arg devTools '[]' --arg benchmarkTools '[]'` since we don't need +# those to build node. +nix-shell \ + --arg loadJSBuiltinsDynamically false \ + --arg devTools '[]' --arg benchmarkTools '[]' \ + --run 'make build-ci -j12' + +mv out/Release/node ./node_old + +# ... +# Make your local changes, and re-build node + +nix-shell \ + --arg loadJSBuiltinsDynamically false \ + --arg devTools '[]' --arg benchmarkTools '[]' \ + --run 'make build-ci -j12' + +nix-shell --pure --run './node benchmark/compare.js --old ./node_old --new ./node http | Rscript benchmark/compare.R' +``` + +There are additional attributes you can pass, see `shell.nix` file for more details. + #### Building Node.js If the path to your build directory contains a space, the build will likely @@ -267,7 +337,6 @@ fail. To build Node.js: ```bash -export CXX=g++-12 ./configure make -j4 ``` @@ -352,6 +421,27 @@ You can also execute the tests in a test suite directory tools/test.py test/message ``` +You can execute tests that match a specific naming pattern using the wildcard +`*`. For example, to run all tests under `test/parallel` with a name that starts +with `test-stream-`: + +```bash +tools/test.py test/parallel/test-stream-* +tools/test.py parallel/test-stream-* # The test/ prefix can be omitted +# In some shell environments, you may need to quote the pattern +tools/test.py "test/parallel/test-stream-*" +``` + +The whildcard `*` can be used in any part of the path. For example, to run all tests +with a name that starts with `test-inspector-`, regardless of the directory they are in: + +```bash +# Matches test/sequential/test-inspector-*, test/parallel/test-inspector-*, +# test/known_issues/test-inspector-*, etc. +tools/test.py "test/*/test-inspector-*" +tools/test.py "*/test-inspector-*" # The test/ prefix can be omitted +``` + If you want to check the other options, please refer to the help by using the `--help` option: diff --git a/CHANGELOG.md b/CHANGELOG.md index b8e1612a0c4eaf..d8df4b8a95fc1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,7 +41,8 @@ release. -25.0.0
+25.1.0
+25.0.0
24.10.0
diff --git a/Makefile b/Makefile index 34f3ed11bfa9a9..bccbac90bca748 100644 --- a/Makefile +++ b/Makefile @@ -399,7 +399,7 @@ ADDONS_HEADERS_PREREQS := tools/install.py \ $(wildcard deps/uv/include/*/*.h) \ $(wildcard deps/v8/include/*.h) \ $(wildcard deps/v8/include/*/*.h) \ - deps/zlib/zconf.h deps/zlib/zlib.h \ + $(wildcard deps/zlib/z*.h) \ src/node.h src/node_api.h src/js_native_api.h src/js_native_api_types.h \ src/node_api_types.h src/node_buffer.h src/node_object_wrap.h \ src/node_version.h @@ -1032,6 +1032,11 @@ override DESTCPU=x86 endif TARNAME=node-$(FULLVERSION) +# Supply SKIP_SHARED_DEPS=1 to explicitly skip all dependencies that can be included as shared deps +SKIP_SHARED_DEPS ?= 0 +ifeq ($(SKIP_SHARED_DEPS), 1) +TARNAME:=$(TARNAME)-slim +endif TARBALL=$(TARNAME).tar # Custom user-specified variation, use it directly ifdef VARIATION @@ -1215,12 +1220,31 @@ $(TARBALL): release-only doc-only $(RM) -r $(TARNAME)/.mailmap $(RM) -r $(TARNAME)/deps/corepack $(RM) $(TARNAME)/test/parallel/test-corepack-version.js +ifeq ($(SKIP_SHARED_DEPS), 1) + $(RM) -r $(TARNAME)/deps/ada + $(RM) -r $(TARNAME)/deps/brotli + $(RM) -r $(TARNAME)/deps/cares + $(RM) -r $(TARNAME)/deps/icu-small + $(RM) -r $(TARNAME)/deps/icu-tmp + $(RM) -r $(TARNAME)/deps/llhttp + $(RM) -r $(TARNAME)/deps/nghttp2 + $(RM) -r $(TARNAME)/deps/ngtcp2 + find $(TARNAME)/deps/openssl -maxdepth 1 -type f ! -name 'nodejs-openssl.cnf' -exec $(RM) {} + + find $(TARNAME)/deps/openssl -mindepth 1 -maxdepth 1 -type d -exec $(RM) -r {} + + $(RM) -r $(TARNAME)/deps/simdjson + $(RM) -r $(TARNAME)/deps/sqlite + $(RM) -r $(TARNAME)/deps/uv + $(RM) -r $(TARNAME)/deps/uvwasi + $(RM) -r $(TARNAME)/deps/zlib + $(RM) -r $(TARNAME)/deps/zstd +else $(RM) -r $(TARNAME)/deps/openssl/openssl/demos $(RM) -r $(TARNAME)/deps/openssl/openssl/doc $(RM) -r $(TARNAME)/deps/openssl/openssl/test $(RM) -r $(TARNAME)/deps/uv/docs $(RM) -r $(TARNAME)/deps/uv/samples $(RM) -r $(TARNAME)/deps/uv/test +endif $(RM) -r $(TARNAME)/deps/v8/samples $(RM) -r $(TARNAME)/deps/v8/tools/profviz $(RM) -r $(TARNAME)/deps/v8/tools/run-tests.py diff --git a/README.md b/README.md index 281420dfc89c23..e7f4d7ba2b4792 100644 --- a/README.md +++ b/README.md @@ -323,8 +323,6 @@ For information about the governance of the Node.js project, see **Erick Wendel** <> (he/him) * [Ethan-Arrowood](https://github.com/Ethan-Arrowood) - **Ethan Arrowood** <> (he/him) -* [F3n67u](https://github.com/F3n67u) - - **Feng Yu** <> (he/him) * [fhinkel](https://github.com/fhinkel) - **Franziska Hinkelmann** <> (she/her) * [Flarna](https://github.com/Flarna) - @@ -527,6 +525,8 @@ For information about the governance of the Node.js project, see **Eugene Ostroukhov** <> * [evanlucas](https://github.com/evanlucas) - **Evan Lucas** <> (he/him) +* [F3n67u](https://github.com/F3n67u) - + **Feng Yu** <> (he/him) * [firedfox](https://github.com/firedfox) - **Daniel Wang** <> * [Fishrock123](https://github.com/Fishrock123) - @@ -751,8 +751,6 @@ maintaining the Node.js project. **Oliver Medhurst** <> (they/them) * [daeyeon](https://github.com/daeyeon) - **Daeyeon Jeong** <> (he/him) -* [F3n67u](https://github.com/F3n67u) - - **Feng Yu** <> (he/him) * [gireeshpunathil](https://github.com/gireeshpunathil) - **Gireesh Punathil** <> (he/him) * [gurgunday](https://github.com/gurgunday) - diff --git a/benchmark/cpu.sh b/benchmark/cpu.sh index 9c9dd7fa4ddc58..7e35c0ef0de8c8 100755 --- a/benchmark/cpu.sh +++ b/benchmark/cpu.sh @@ -4,21 +4,73 @@ CPUPATH=/sys/devices/system/cpu MAXID=$(cat $CPUPATH/present | awk -F- '{print $NF}') +[ "$(uname -s || true)" = "Linux" ] || \ + echo "Warning: This script supports Linux only." >&2 + +[ "$(id -u || true)" = "0" ] || \ + echo "Warning: This script typically needs root access to modify CPU governor. Consider running it with sudo." >&2 + +get_default_governor() { + case "$(cat "$CPUPATH/cpu0/cpufreq/scaling_available_governors")" in + *"schedutil"*) echo "schedutil" ;; + *"ondemand"*) echo "ondemand" ;; + *"conservative"*) echo "conservative";; + *) echo "powersave" ;; + esac + +} + set_governor() { - echo "Setting CPU frequency governor to \"$1\"" + governor_name="$1" + + echo "Setting governor for all CPU cores to \"$governor_name\"..." + i=0 while [ "$i" -le "$MAXID" ]; do echo "$1" > "$CPUPATH/cpu$i/cpufreq/scaling_governor" i=$((i + 1)) done + + echo "Done." +} + +usage() { + default_gov=$(get_default_governor) + echo "CPU Governor Management Script" + echo "----------------------------------------------------------------------------" + echo "Usage: $0 [command]" + echo + echo "Commands:" + echo " fast Sets the governor to 'performance' for maximum speed." + echo " (Warning: Increases heat/power use. Use for short-term tasks.)" + echo + echo " reset Resets the governor to the system's recommended default ('$default_gov')." + echo + echo " get Shows the current CPU governor for ALL cores." + echo "----------------------------------------------------------------------------" } case "$1" in fast | performance) + echo "Warning: The 'performance' mode locks the CPU at its highest speed." + echo "It is highly recommended to 'reset' after your task is complete." set_governor "performance" ;; + + reset | default) + default_governor=$(get_default_governor) + set_governor "$default_governor" + ;; + + get | status) + echo "Current governor status for all cores:" + grep . "$CPUPATH"/cpu*/cpufreq/scaling_governor + ;; + *) - echo "Usage: $0 fast" + usage exit 1 ;; esac + +exit 0 diff --git a/benchmark/esm/import-esm-reload.js b/benchmark/esm/import-esm-reload.js new file mode 100644 index 00000000000000..40143930b04ed8 --- /dev/null +++ b/benchmark/esm/import-esm-reload.js @@ -0,0 +1,37 @@ +'use strict'; + +const common = require('../common'); +const fs = require('fs'); +const tmpdir = require('../../test/common/tmpdir'); +const assert = require('assert'); +const { pathToFileURL } = require('url'); +const bench = common.createBenchmark(main, { + count: [1, 100], + n: [1000], +}); + +function prepare(count) { + tmpdir.refresh(); + const dir = tmpdir.resolve('modules'); + fs.mkdirSync(dir, { recursive: true }); + let modSource = ''; + for (let i = 0; i < count; ++i) { + modSource += `export const value${i} = 1;\n`; + } + const script = tmpdir.resolve('mod.js'); + fs.writeFileSync(script, modSource, 'utf8'); + return script; +} + +async function main({ n, count }) { + const script = prepare(count); + const url = pathToFileURL(script).href; + let result = 0; + bench.start(); + for (let i = 0; i < n; i++) { + const mod = await import(`${url}?t=${i}`); + result += mod[`value${count - 1}`]; + } + bench.end(n); + assert.strictEqual(result, n); +} diff --git a/benchmark/vm/source-text-module-chained.js b/benchmark/vm/source-text-module-chained.js new file mode 100644 index 00000000000000..691af2561883e5 --- /dev/null +++ b/benchmark/vm/source-text-module-chained.js @@ -0,0 +1,48 @@ +'use strict'; + +const vm = require('vm'); +const common = require('../common.js'); +const assert = require('assert'); + +const bench = common.createBenchmark(main, { + stage: ['all', 'instantiate', 'evaluate'], + n: [1000], +}, { + flags: ['--experimental-vm-modules'], +}); + +function main({ stage, n }) { + const arr = [new vm.SourceTextModule(` + export const value = 42; + `)]; + + if (stage === 'all') { + bench.start(); + } + + for (let i = 0; i < n; i++) { + const m = new vm.SourceTextModule(` + export { value } from 'mod${i}'; + `); + arr.push(m); + m.linkRequests([arr[i]]); + } + + if (stage === 'instantiate') { + bench.start(); + } + arr[n].instantiate(); + if (stage === 'instantiate') { + bench.end(n); + } + + if (stage === 'evaluate') { + bench.start(); + } + arr[n].evaluate(); + if (stage === 'evaluate' || stage === 'all') { + bench.end(n); + } + + assert.strictEqual(arr[n].namespace.value, 42); +} diff --git a/benchmark/vm/source-text-module-flat.js b/benchmark/vm/source-text-module-flat.js new file mode 100644 index 00000000000000..06acacb25a550c --- /dev/null +++ b/benchmark/vm/source-text-module-flat.js @@ -0,0 +1,68 @@ +'use strict'; + +const vm = require('vm'); +const common = require('../common.js'); +const assert = require('assert'); + +const bench = common.createBenchmark(main, { + stage: ['all', 'link', 'instantiate', 'evaluate'], + n: [1000], +}, { + flags: ['--experimental-vm-modules'], +}); + +function main({ stage, n }) { + const arr = []; + let importSource = ''; + let useSource = 'export const result = 0 '; + for (let i = 0; i < n; i++) { + importSource += `import { value${i} } from 'mod${i}';\n`; + useSource += ` + value${i}\n`; + } + + if (stage === 'all') { + bench.start(); + } + for (let i = 0; i < n; i++) { + const m = new vm.SourceTextModule(` + export const value${i} = 1; + `); + arr.push(m); + } + + const root = new vm.SourceTextModule(` + ${importSource} + ${useSource}; + `); + + if (stage === 'link') { + bench.start(); + } + + root.linkRequests(arr); + for (let i = 0; i < n; i++) { + arr[i].linkRequests([]); + } + + if (stage === 'link') { + bench.end(n); + } + + if (stage === 'instantiate') { + bench.start(); + } + root.instantiate(); + if (stage === 'instantiate') { + bench.end(n); + } + + if (stage === 'evaluate') { + bench.start(); + } + root.evaluate(); + if (stage === 'evaluate' || stage === 'all') { + bench.end(n); + } + + assert.strictEqual(root.namespace.result, n); +} diff --git a/benchmark/vm/source-text-module-leaf.js b/benchmark/vm/source-text-module-leaf.js new file mode 100644 index 00000000000000..66d942556c6ac8 --- /dev/null +++ b/benchmark/vm/source-text-module-leaf.js @@ -0,0 +1,84 @@ +'use strict'; + +const vm = require('vm'); +const common = require('../common.js'); +const assert = require('assert'); + +const bench = common.createBenchmark(main, { + stage: ['all', 'compile', 'link', 'instantiate', 'evaluate'], + type: ['sync', 'async'], + n: [1000], +}, { + flags: ['--experimental-vm-modules'], +}); + +function main({ stage, type, n }) { + const arr = []; + if (stage === 'all' || stage === 'compile') { + bench.start(); + } + + for (let i = 0; i < n; i++) { + let source = `export const value${i} = 1;`; + if (type === 'async') { + source += `\nawait Promise.resolve();\n`; + } + const m = new vm.SourceTextModule(source); + arr.push(m); + } + + if (stage === 'compile') { + bench.end(n); + return; + } + + if (stage === 'link') { + bench.start(); + } + + for (let i = 0; i < n; i++) { + arr[i].linkRequests([]); + } + + if (stage === 'link') { + bench.end(n); + return; + } + + if (stage === 'instantiate') { + bench.start(); + } + + for (let i = 0; i < n; i++) { + arr[i].instantiate(); + } + + if (stage === 'instantiate') { + bench.end(n); + return; + } + + if (stage === 'evaluate') { + bench.start(); + } + + function finalize() { + bench.end(n); + for (let i = 0; i < n; i++) { + assert.strictEqual(arr[i].namespace[`value${i}`], 1); + } + } + + if (type === 'sync') { + for (let i = 0; i < n; i++) { + arr[i].evaluate(); + } + finalize(); + } else { + const promises = []; + for (let i = 0; i < n; i++) { + promises.push(arr[i].evaluate()); + } + Promise.all(promises).then(finalize); + } +} diff --git a/common.gypi b/common.gypi index 592c4e9294e05f..55476f5ce2b184 100644 --- a/common.gypi +++ b/common.gypi @@ -38,7 +38,7 @@ # Reset this number to 0 on major V8 upgrades. # Increment by one for each non-official patch applied to deps/v8. - 'v8_embedder_string': '-node.10', + 'v8_embedder_string': '-node.11', ##### V8 defaults for Node.js ##### @@ -443,12 +443,12 @@ ['v8_enable_pointer_compression == 1', { 'defines': ['V8_COMPRESS_POINTERS'], }], + ['v8_enable_pointer_compression == 1 and v8_enable_pointer_compression_shared_cage != 1', { + 'defines': ['V8_COMPRESS_POINTERS_IN_MULTIPLE_CAGES'], + }], ['v8_enable_pointer_compression_shared_cage == 1', { 'defines': ['V8_COMPRESS_POINTERS_IN_SHARED_CAGE'], }], - ['v8_enable_pointer_compression == 1 and v8_enable_pointer_compression_shared_cage != 1', { - 'defines': ['V8_COMPRESS_POINTERS_IN_ISOLATE_CAGE'], - }], ['v8_enable_pointer_compression == 1 or v8_enable_31bit_smis_on_64bit_arch == 1', { 'defines': ['V8_31BIT_SMIS_ON_64BIT_ARCH'], }], diff --git a/configure.py b/configure.py index 5a05a1fdcd496c..a752897e90aa25 100755 --- a/configure.py +++ b/configure.py @@ -646,6 +646,12 @@ default=None, help='[Experimental] Enable V8 pointer compression (limits max heap to 4GB and breaks ABI compatibility)') +parser.add_argument('--experimental-pointer-compression-shared-cage', + action='store_true', + dest='pointer_compression_shared_cage', + default=None, + help='[Experimental] Use V8 pointer compression with shared cage (requires --experimental-enable-pointer-compression)') + parser.add_argument('--v8-options', action='store', dest='v8_options', @@ -1789,7 +1795,10 @@ def configure_v8(o, configs): # Note that enabling pointer compression without enabling sandbox is unsupported by V8, # so this can be broken at any time. o['variables']['v8_enable_sandbox'] = 0 - o['variables']['v8_enable_pointer_compression_shared_cage'] = 1 if options.enable_pointer_compression else 0 + # We set v8_enable_pointer_compression_shared_cage to 0 always, even when + # pointer compression is enabled so that we don't accidentally enable shared + # cage mode when pointer compression is on. + o['variables']['v8_enable_pointer_compression_shared_cage'] = 1 if options.pointer_compression_shared_cage else 0 o['variables']['v8_enable_external_code_space'] = 1 if options.enable_pointer_compression else 0 o['variables']['v8_enable_31bit_smis_on_64bit_arch'] = 1 if options.enable_pointer_compression else 0 o['variables']['v8_enable_extensible_ro_snapshot'] = 0 diff --git a/deps/corepack/CHANGELOG.md b/deps/corepack/CHANGELOG.md index fdf937785ca5d4..61ad4b5379818c 100644 --- a/deps/corepack/CHANGELOG.md +++ b/deps/corepack/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## [0.34.1](https://github.com/nodejs/corepack/compare/v0.34.0...v0.34.1) (2025-10-17) + + +### Bug Fixes + +* incorrect registry origin check ([#743](https://github.com/nodejs/corepack/issues/743)) ([cc840b2](https://github.com/nodejs/corepack/commit/cc840b2d232a29c225d2436d350640f0035ed28b)) +* update package manager versions ([#728](https://github.com/nodejs/corepack/issues/728)) ([78ce029](https://github.com/nodejs/corepack/commit/78ce0297a9152bb5c68f724821a9a0095b408334)) + ## [0.34.0](https://github.com/nodejs/corepack/compare/v0.33.0...v0.34.0) (2025-07-19) diff --git a/deps/corepack/dist/lib/corepack.cjs b/deps/corepack/dist/lib/corepack.cjs index 78fb4372832e3b..8a5add55f0083c 100644 --- a/deps/corepack/dist/lib/corepack.cjs +++ b/deps/corepack/dist/lib/corepack.cjs @@ -15,11 +15,11 @@ var __export = (target, all) => { for (var name2 in all) __defProp(target, name2, { get: all[name2], enumerable: true }); }; -var __copyProps = (to, from, except, desc) => { +var __copyProps = (to, from, except, desc2) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) - __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + __defProp(to, key, { get: () => from[key], enumerable: !(desc2 = __getOwnPropDesc(from, key)) || desc2.enumerable }); } return to; }; @@ -604,12 +604,12 @@ function as(value, validator, { coerce = false, errors: storeErrors, throw: thro } function fn(validators, fn2) { const isValidArgList = isTuple(validators); - return (...args) => { + return ((...args) => { const check = isValidArgList(args); if (!check) throw new TypeAssertionError(); return fn2(...args); - }; + }); } function hasMinLength(length) { return makeValidator({ @@ -1090,18 +1090,20 @@ var require_node = __commonJS({ } }); -// .yarn/cache/semver-npm-7.7.1-4572475307-fd603a6fb9.zip/node_modules/semver/internal/debug.js +// .yarn/cache/semver-npm-7.7.3-9cf7b3b46c-4afe5c9865.zip/node_modules/semver/internal/debug.js var require_debug = __commonJS({ - ".yarn/cache/semver-npm-7.7.1-4572475307-fd603a6fb9.zip/node_modules/semver/internal/debug.js"(exports2, module2) { + ".yarn/cache/semver-npm-7.7.3-9cf7b3b46c-4afe5c9865.zip/node_modules/semver/internal/debug.js"(exports2, module2) { + "use strict"; var debug2 = typeof process === "object" && process.env && process.env.NODE_DEBUG && /\bsemver\b/i.test(process.env.NODE_DEBUG) ? (...args) => console.error("SEMVER", ...args) : () => { }; module2.exports = debug2; } }); -// .yarn/cache/semver-npm-7.7.1-4572475307-fd603a6fb9.zip/node_modules/semver/internal/constants.js +// .yarn/cache/semver-npm-7.7.3-9cf7b3b46c-4afe5c9865.zip/node_modules/semver/internal/constants.js var require_constants = __commonJS({ - ".yarn/cache/semver-npm-7.7.1-4572475307-fd603a6fb9.zip/node_modules/semver/internal/constants.js"(exports2, module2) { + ".yarn/cache/semver-npm-7.7.3-9cf7b3b46c-4afe5c9865.zip/node_modules/semver/internal/constants.js"(exports2, module2) { + "use strict"; var SEMVER_SPEC_VERSION = "2.0.0"; var MAX_LENGTH = 256; var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || /* istanbul ignore next */ @@ -1130,9 +1132,10 @@ var require_constants = __commonJS({ } }); -// .yarn/cache/semver-npm-7.7.1-4572475307-fd603a6fb9.zip/node_modules/semver/internal/re.js +// .yarn/cache/semver-npm-7.7.3-9cf7b3b46c-4afe5c9865.zip/node_modules/semver/internal/re.js var require_re = __commonJS({ - ".yarn/cache/semver-npm-7.7.1-4572475307-fd603a6fb9.zip/node_modules/semver/internal/re.js"(exports2, module2) { + ".yarn/cache/semver-npm-7.7.3-9cf7b3b46c-4afe5c9865.zip/node_modules/semver/internal/re.js"(exports2, module2) { + "use strict"; var { MAX_SAFE_COMPONENT_LENGTH, MAX_SAFE_BUILD_LENGTH, @@ -1173,8 +1176,8 @@ var require_re = __commonJS({ createToken("NONNUMERICIDENTIFIER", `\\d*[a-zA-Z-]${LETTERDASHNUMBER}*`); createToken("MAINVERSION", `(${src[t.NUMERICIDENTIFIER]})\\.(${src[t.NUMERICIDENTIFIER]})\\.(${src[t.NUMERICIDENTIFIER]})`); createToken("MAINVERSIONLOOSE", `(${src[t.NUMERICIDENTIFIERLOOSE]})\\.(${src[t.NUMERICIDENTIFIERLOOSE]})\\.(${src[t.NUMERICIDENTIFIERLOOSE]})`); - createToken("PRERELEASEIDENTIFIER", `(?:${src[t.NUMERICIDENTIFIER]}|${src[t.NONNUMERICIDENTIFIER]})`); - createToken("PRERELEASEIDENTIFIERLOOSE", `(?:${src[t.NUMERICIDENTIFIERLOOSE]}|${src[t.NONNUMERICIDENTIFIER]})`); + createToken("PRERELEASEIDENTIFIER", `(?:${src[t.NONNUMERICIDENTIFIER]}|${src[t.NUMERICIDENTIFIER]})`); + createToken("PRERELEASEIDENTIFIERLOOSE", `(?:${src[t.NONNUMERICIDENTIFIER]}|${src[t.NUMERICIDENTIFIERLOOSE]})`); createToken("PRERELEASE", `(?:-(${src[t.PRERELEASEIDENTIFIER]}(?:\\.${src[t.PRERELEASEIDENTIFIER]})*))`); createToken("PRERELEASELOOSE", `(?:-?(${src[t.PRERELEASEIDENTIFIERLOOSE]}(?:\\.${src[t.PRERELEASEIDENTIFIERLOOSE]})*))`); createToken("BUILDIDENTIFIER", `${LETTERDASHNUMBER}+`); @@ -1217,9 +1220,10 @@ var require_re = __commonJS({ } }); -// .yarn/cache/semver-npm-7.7.1-4572475307-fd603a6fb9.zip/node_modules/semver/internal/parse-options.js +// .yarn/cache/semver-npm-7.7.3-9cf7b3b46c-4afe5c9865.zip/node_modules/semver/internal/parse-options.js var require_parse_options = __commonJS({ - ".yarn/cache/semver-npm-7.7.1-4572475307-fd603a6fb9.zip/node_modules/semver/internal/parse-options.js"(exports2, module2) { + ".yarn/cache/semver-npm-7.7.3-9cf7b3b46c-4afe5c9865.zip/node_modules/semver/internal/parse-options.js"(exports2, module2) { + "use strict"; var looseOption = Object.freeze({ loose: true }); var emptyOpts = Object.freeze({}); var parseOptions = (options) => { @@ -1235,11 +1239,15 @@ var require_parse_options = __commonJS({ } }); -// .yarn/cache/semver-npm-7.7.1-4572475307-fd603a6fb9.zip/node_modules/semver/internal/identifiers.js +// .yarn/cache/semver-npm-7.7.3-9cf7b3b46c-4afe5c9865.zip/node_modules/semver/internal/identifiers.js var require_identifiers = __commonJS({ - ".yarn/cache/semver-npm-7.7.1-4572475307-fd603a6fb9.zip/node_modules/semver/internal/identifiers.js"(exports2, module2) { + ".yarn/cache/semver-npm-7.7.3-9cf7b3b46c-4afe5c9865.zip/node_modules/semver/internal/identifiers.js"(exports2, module2) { + "use strict"; var numeric = /^[0-9]+$/; var compareIdentifiers = (a, b) => { + if (typeof a === "number" && typeof b === "number") { + return a === b ? 0 : a < b ? -1 : 1; + } const anum = numeric.test(a); const bnum = numeric.test(b); if (anum && bnum) { @@ -1256,40 +1264,41 @@ var require_identifiers = __commonJS({ } }); -// .yarn/cache/semver-npm-7.7.1-4572475307-fd603a6fb9.zip/node_modules/semver/classes/semver.js +// .yarn/cache/semver-npm-7.7.3-9cf7b3b46c-4afe5c9865.zip/node_modules/semver/classes/semver.js var require_semver = __commonJS({ - ".yarn/cache/semver-npm-7.7.1-4572475307-fd603a6fb9.zip/node_modules/semver/classes/semver.js"(exports2, module2) { + ".yarn/cache/semver-npm-7.7.3-9cf7b3b46c-4afe5c9865.zip/node_modules/semver/classes/semver.js"(exports2, module2) { + "use strict"; var debug2 = require_debug(); var { MAX_LENGTH, MAX_SAFE_INTEGER } = require_constants(); - var { safeRe: re, safeSrc: src, t } = require_re(); + var { safeRe: re, t } = require_re(); var parseOptions = require_parse_options(); var { compareIdentifiers } = require_identifiers(); var SemVer3 = class _SemVer { - constructor(version3, options) { + constructor(version2, options) { options = parseOptions(options); - if (version3 instanceof _SemVer) { - if (version3.loose === !!options.loose && version3.includePrerelease === !!options.includePrerelease) { - return version3; + if (version2 instanceof _SemVer) { + if (version2.loose === !!options.loose && version2.includePrerelease === !!options.includePrerelease) { + return version2; } else { - version3 = version3.version; + version2 = version2.version; } - } else if (typeof version3 !== "string") { - throw new TypeError(`Invalid version. Must be a string. Got type "${typeof version3}".`); + } else if (typeof version2 !== "string") { + throw new TypeError(`Invalid version. Must be a string. Got type "${typeof version2}".`); } - if (version3.length > MAX_LENGTH) { + if (version2.length > MAX_LENGTH) { throw new TypeError( `version is longer than ${MAX_LENGTH} characters` ); } - debug2("SemVer", version3, options); + debug2("SemVer", version2, options); this.options = options; this.loose = !!options.loose; this.includePrerelease = !!options.includePrerelease; - const m = version3.trim().match(options.loose ? re[t.LOOSE] : re[t.FULL]); + const m = version2.trim().match(options.loose ? re[t.LOOSE] : re[t.FULL]); if (!m) { - throw new TypeError(`Invalid Version: ${version3}`); + throw new TypeError(`Invalid Version: ${version2}`); } - this.raw = version3; + this.raw = version2; this.major = +m[1]; this.minor = +m[2]; this.patch = +m[3]; @@ -1345,7 +1354,25 @@ var require_semver = __commonJS({ if (!(other instanceof _SemVer)) { other = new _SemVer(other, this.options); } - return compareIdentifiers(this.major, other.major) || compareIdentifiers(this.minor, other.minor) || compareIdentifiers(this.patch, other.patch); + if (this.major < other.major) { + return -1; + } + if (this.major > other.major) { + return 1; + } + if (this.minor < other.minor) { + return -1; + } + if (this.minor > other.minor) { + return 1; + } + if (this.patch < other.patch) { + return -1; + } + if (this.patch > other.patch) { + return 1; + } + return 0; } comparePre(other) { if (!(other instanceof _SemVer)) { @@ -1406,8 +1433,7 @@ var require_semver = __commonJS({ throw new Error("invalid increment argument: identifier is empty"); } if (identifier) { - const r = new RegExp(`^${this.options.loose ? src[t.PRERELEASELOOSE] : src[t.PRERELEASE]}$`); - const match = `-${identifier}`.match(r); + const match = `-${identifier}`.match(this.options.loose ? re[t.PRERELEASELOOSE] : re[t.PRERELEASE]); if (!match || match[1] !== identifier) { throw new Error(`invalid identifier: ${identifier}`); } @@ -1517,34 +1543,37 @@ var require_semver = __commonJS({ } }); -// .yarn/cache/semver-npm-7.7.1-4572475307-fd603a6fb9.zip/node_modules/semver/functions/compare.js +// .yarn/cache/semver-npm-7.7.3-9cf7b3b46c-4afe5c9865.zip/node_modules/semver/functions/compare.js var require_compare = __commonJS({ - ".yarn/cache/semver-npm-7.7.1-4572475307-fd603a6fb9.zip/node_modules/semver/functions/compare.js"(exports2, module2) { + ".yarn/cache/semver-npm-7.7.3-9cf7b3b46c-4afe5c9865.zip/node_modules/semver/functions/compare.js"(exports2, module2) { + "use strict"; var SemVer3 = require_semver(); var compare = (a, b, loose) => new SemVer3(a, loose).compare(new SemVer3(b, loose)); module2.exports = compare; } }); -// .yarn/cache/semver-npm-7.7.1-4572475307-fd603a6fb9.zip/node_modules/semver/functions/rcompare.js +// .yarn/cache/semver-npm-7.7.3-9cf7b3b46c-4afe5c9865.zip/node_modules/semver/functions/rcompare.js var require_rcompare = __commonJS({ - ".yarn/cache/semver-npm-7.7.1-4572475307-fd603a6fb9.zip/node_modules/semver/functions/rcompare.js"(exports2, module2) { + ".yarn/cache/semver-npm-7.7.3-9cf7b3b46c-4afe5c9865.zip/node_modules/semver/functions/rcompare.js"(exports2, module2) { + "use strict"; var compare = require_compare(); var rcompare = (a, b, loose) => compare(b, a, loose); module2.exports = rcompare; } }); -// .yarn/cache/semver-npm-7.7.1-4572475307-fd603a6fb9.zip/node_modules/semver/functions/parse.js +// .yarn/cache/semver-npm-7.7.3-9cf7b3b46c-4afe5c9865.zip/node_modules/semver/functions/parse.js var require_parse = __commonJS({ - ".yarn/cache/semver-npm-7.7.1-4572475307-fd603a6fb9.zip/node_modules/semver/functions/parse.js"(exports2, module2) { + ".yarn/cache/semver-npm-7.7.3-9cf7b3b46c-4afe5c9865.zip/node_modules/semver/functions/parse.js"(exports2, module2) { + "use strict"; var SemVer3 = require_semver(); - var parse5 = (version3, options, throwErrors = false) => { - if (version3 instanceof SemVer3) { - return version3; + var parse4 = (version2, options, throwErrors = false) => { + if (version2 instanceof SemVer3) { + return version2; } try { - return new SemVer3(version3, options); + return new SemVer3(version2, options); } catch (er) { if (!throwErrors) { return null; @@ -1552,25 +1581,27 @@ var require_parse = __commonJS({ throw er; } }; - module2.exports = parse5; + module2.exports = parse4; } }); -// .yarn/cache/semver-npm-7.7.1-4572475307-fd603a6fb9.zip/node_modules/semver/functions/valid.js +// .yarn/cache/semver-npm-7.7.3-9cf7b3b46c-4afe5c9865.zip/node_modules/semver/functions/valid.js var require_valid = __commonJS({ - ".yarn/cache/semver-npm-7.7.1-4572475307-fd603a6fb9.zip/node_modules/semver/functions/valid.js"(exports2, module2) { - var parse5 = require_parse(); - var valid = (version3, options) => { - const v = parse5(version3, options); + ".yarn/cache/semver-npm-7.7.3-9cf7b3b46c-4afe5c9865.zip/node_modules/semver/functions/valid.js"(exports2, module2) { + "use strict"; + var parse4 = require_parse(); + var valid = (version2, options) => { + const v = parse4(version2, options); return v ? v.version : null; }; module2.exports = valid; } }); -// .yarn/cache/semver-npm-7.7.1-4572475307-fd603a6fb9.zip/node_modules/semver/internal/lrucache.js +// .yarn/cache/semver-npm-7.7.3-9cf7b3b46c-4afe5c9865.zip/node_modules/semver/internal/lrucache.js var require_lrucache = __commonJS({ - ".yarn/cache/semver-npm-7.7.1-4572475307-fd603a6fb9.zip/node_modules/semver/internal/lrucache.js"(exports2, module2) { + ".yarn/cache/semver-npm-7.7.3-9cf7b3b46c-4afe5c9865.zip/node_modules/semver/internal/lrucache.js"(exports2, module2) { + "use strict"; var LRUCache = class { constructor() { this.max = 1e3; @@ -1605,63 +1636,70 @@ var require_lrucache = __commonJS({ } }); -// .yarn/cache/semver-npm-7.7.1-4572475307-fd603a6fb9.zip/node_modules/semver/functions/eq.js +// .yarn/cache/semver-npm-7.7.3-9cf7b3b46c-4afe5c9865.zip/node_modules/semver/functions/eq.js var require_eq = __commonJS({ - ".yarn/cache/semver-npm-7.7.1-4572475307-fd603a6fb9.zip/node_modules/semver/functions/eq.js"(exports2, module2) { + ".yarn/cache/semver-npm-7.7.3-9cf7b3b46c-4afe5c9865.zip/node_modules/semver/functions/eq.js"(exports2, module2) { + "use strict"; var compare = require_compare(); var eq = (a, b, loose) => compare(a, b, loose) === 0; module2.exports = eq; } }); -// .yarn/cache/semver-npm-7.7.1-4572475307-fd603a6fb9.zip/node_modules/semver/functions/neq.js +// .yarn/cache/semver-npm-7.7.3-9cf7b3b46c-4afe5c9865.zip/node_modules/semver/functions/neq.js var require_neq = __commonJS({ - ".yarn/cache/semver-npm-7.7.1-4572475307-fd603a6fb9.zip/node_modules/semver/functions/neq.js"(exports2, module2) { + ".yarn/cache/semver-npm-7.7.3-9cf7b3b46c-4afe5c9865.zip/node_modules/semver/functions/neq.js"(exports2, module2) { + "use strict"; var compare = require_compare(); var neq = (a, b, loose) => compare(a, b, loose) !== 0; module2.exports = neq; } }); -// .yarn/cache/semver-npm-7.7.1-4572475307-fd603a6fb9.zip/node_modules/semver/functions/gt.js +// .yarn/cache/semver-npm-7.7.3-9cf7b3b46c-4afe5c9865.zip/node_modules/semver/functions/gt.js var require_gt = __commonJS({ - ".yarn/cache/semver-npm-7.7.1-4572475307-fd603a6fb9.zip/node_modules/semver/functions/gt.js"(exports2, module2) { + ".yarn/cache/semver-npm-7.7.3-9cf7b3b46c-4afe5c9865.zip/node_modules/semver/functions/gt.js"(exports2, module2) { + "use strict"; var compare = require_compare(); var gt = (a, b, loose) => compare(a, b, loose) > 0; module2.exports = gt; } }); -// .yarn/cache/semver-npm-7.7.1-4572475307-fd603a6fb9.zip/node_modules/semver/functions/gte.js +// .yarn/cache/semver-npm-7.7.3-9cf7b3b46c-4afe5c9865.zip/node_modules/semver/functions/gte.js var require_gte = __commonJS({ - ".yarn/cache/semver-npm-7.7.1-4572475307-fd603a6fb9.zip/node_modules/semver/functions/gte.js"(exports2, module2) { + ".yarn/cache/semver-npm-7.7.3-9cf7b3b46c-4afe5c9865.zip/node_modules/semver/functions/gte.js"(exports2, module2) { + "use strict"; var compare = require_compare(); var gte = (a, b, loose) => compare(a, b, loose) >= 0; module2.exports = gte; } }); -// .yarn/cache/semver-npm-7.7.1-4572475307-fd603a6fb9.zip/node_modules/semver/functions/lt.js +// .yarn/cache/semver-npm-7.7.3-9cf7b3b46c-4afe5c9865.zip/node_modules/semver/functions/lt.js var require_lt = __commonJS({ - ".yarn/cache/semver-npm-7.7.1-4572475307-fd603a6fb9.zip/node_modules/semver/functions/lt.js"(exports2, module2) { + ".yarn/cache/semver-npm-7.7.3-9cf7b3b46c-4afe5c9865.zip/node_modules/semver/functions/lt.js"(exports2, module2) { + "use strict"; var compare = require_compare(); var lt = (a, b, loose) => compare(a, b, loose) < 0; module2.exports = lt; } }); -// .yarn/cache/semver-npm-7.7.1-4572475307-fd603a6fb9.zip/node_modules/semver/functions/lte.js +// .yarn/cache/semver-npm-7.7.3-9cf7b3b46c-4afe5c9865.zip/node_modules/semver/functions/lte.js var require_lte = __commonJS({ - ".yarn/cache/semver-npm-7.7.1-4572475307-fd603a6fb9.zip/node_modules/semver/functions/lte.js"(exports2, module2) { + ".yarn/cache/semver-npm-7.7.3-9cf7b3b46c-4afe5c9865.zip/node_modules/semver/functions/lte.js"(exports2, module2) { + "use strict"; var compare = require_compare(); var lte = (a, b, loose) => compare(a, b, loose) <= 0; module2.exports = lte; } }); -// .yarn/cache/semver-npm-7.7.1-4572475307-fd603a6fb9.zip/node_modules/semver/functions/cmp.js +// .yarn/cache/semver-npm-7.7.3-9cf7b3b46c-4afe5c9865.zip/node_modules/semver/functions/cmp.js var require_cmp = __commonJS({ - ".yarn/cache/semver-npm-7.7.1-4572475307-fd603a6fb9.zip/node_modules/semver/functions/cmp.js"(exports2, module2) { + ".yarn/cache/semver-npm-7.7.3-9cf7b3b46c-4afe5c9865.zip/node_modules/semver/functions/cmp.js"(exports2, module2) { + "use strict"; var eq = require_eq(); var neq = require_neq(); var gt = require_gt(); @@ -1708,9 +1746,10 @@ var require_cmp = __commonJS({ } }); -// .yarn/cache/semver-npm-7.7.1-4572475307-fd603a6fb9.zip/node_modules/semver/classes/comparator.js +// .yarn/cache/semver-npm-7.7.3-9cf7b3b46c-4afe5c9865.zip/node_modules/semver/classes/comparator.js var require_comparator = __commonJS({ - ".yarn/cache/semver-npm-7.7.1-4572475307-fd603a6fb9.zip/node_modules/semver/classes/comparator.js"(exports2, module2) { + ".yarn/cache/semver-npm-7.7.3-9cf7b3b46c-4afe5c9865.zip/node_modules/semver/classes/comparator.js"(exports2, module2) { + "use strict"; var ANY = Symbol("SemVer ANY"); var Comparator = class _Comparator { static get ANY() { @@ -1756,19 +1795,19 @@ var require_comparator = __commonJS({ toString() { return this.value; } - test(version3) { - debug2("Comparator.test", version3, this.options.loose); - if (this.semver === ANY || version3 === ANY) { + test(version2) { + debug2("Comparator.test", version2, this.options.loose); + if (this.semver === ANY || version2 === ANY) { return true; } - if (typeof version3 === "string") { + if (typeof version2 === "string") { try { - version3 = new SemVer3(version3, this.options); + version2 = new SemVer3(version2, this.options); } catch (er) { return false; } } - return cmp(version3, this.operator, this.semver, this.options); + return cmp(version2, this.operator, this.semver, this.options); } intersects(comp, options) { if (!(comp instanceof _Comparator)) { @@ -1820,9 +1859,10 @@ var require_comparator = __commonJS({ } }); -// .yarn/cache/semver-npm-7.7.1-4572475307-fd603a6fb9.zip/node_modules/semver/classes/range.js +// .yarn/cache/semver-npm-7.7.3-9cf7b3b46c-4afe5c9865.zip/node_modules/semver/classes/range.js var require_range = __commonJS({ - ".yarn/cache/semver-npm-7.7.1-4572475307-fd603a6fb9.zip/node_modules/semver/classes/range.js"(exports2, module2) { + ".yarn/cache/semver-npm-7.7.3-9cf7b3b46c-4afe5c9865.zip/node_modules/semver/classes/range.js"(exports2, module2) { + "use strict"; var SPACE_CHARACTERS = /\s+/g; var Range3 = class _Range { constructor(range, options) { @@ -1891,7 +1931,7 @@ var require_range = __commonJS({ parseRange(range) { const memoOpts = (this.options.includePrerelease && FLAG_INCLUDE_PRERELEASE) | (this.options.loose && FLAG_LOOSE); const memoKey = memoOpts + ":" + range; - const cached = cache.get(memoKey); + const cached = cache2.get(memoKey); if (cached) { return cached; } @@ -1925,7 +1965,7 @@ var require_range = __commonJS({ rangeMap.delete(""); } const result = [...rangeMap.values()]; - cache.set(memoKey, result); + cache2.set(memoKey, result); return result; } intersects(range, options) { @@ -1943,19 +1983,19 @@ var require_range = __commonJS({ }); } // if ANY of the sets match ALL of its comparators, then pass - test(version3) { - if (!version3) { + test(version2) { + if (!version2) { return false; } - if (typeof version3 === "string") { + if (typeof version2 === "string") { try { - version3 = new SemVer3(version3, this.options); + version2 = new SemVer3(version2, this.options); } catch (er) { return false; } } for (let i = 0; i < this.set.length; i++) { - if (testSet(this.set[i], version3, this.options)) { + if (testSet(this.set[i], version2, this.options)) { return true; } } @@ -1964,7 +2004,7 @@ var require_range = __commonJS({ }; module2.exports = Range3; var LRU = require_lrucache(); - var cache = new LRU(); + var cache2 = new LRU(); var parseOptions = require_parse_options(); var Comparator = require_comparator(); var debug2 = require_debug(); @@ -1992,6 +2032,7 @@ var require_range = __commonJS({ return result; }; var parseComparator = (comp, options) => { + comp = comp.replace(re[t.BUILD], ""); debug2("comp", comp, options); comp = replaceCarets(comp, options); debug2("caret", comp); @@ -2169,13 +2210,13 @@ var require_range = __commonJS({ } return `${from} ${to}`.trim(); }; - var testSet = (set, version3, options) => { + var testSet = (set, version2, options) => { for (let i = 0; i < set.length; i++) { - if (!set[i].test(version3)) { + if (!set[i].test(version2)) { return false; } } - if (version3.prerelease.length && !options.includePrerelease) { + if (version2.prerelease.length && !options.includePrerelease) { for (let i = 0; i < set.length; i++) { debug2(set[i].semver); if (set[i].semver === Comparator.ANY) { @@ -2183,7 +2224,7 @@ var require_range = __commonJS({ } if (set[i].semver.prerelease.length > 0) { const allowed = set[i].semver; - if (allowed.major === version3.major && allowed.minor === version3.minor && allowed.patch === version3.patch) { + if (allowed.major === version2.major && allowed.minor === version2.minor && allowed.patch === version2.patch) { return true; } } @@ -2195,9 +2236,10 @@ var require_range = __commonJS({ } }); -// .yarn/cache/semver-npm-7.7.1-4572475307-fd603a6fb9.zip/node_modules/semver/ranges/valid.js +// .yarn/cache/semver-npm-7.7.3-9cf7b3b46c-4afe5c9865.zip/node_modules/semver/ranges/valid.js var require_valid2 = __commonJS({ - ".yarn/cache/semver-npm-7.7.1-4572475307-fd603a6fb9.zip/node_modules/semver/ranges/valid.js"(exports2, module2) { + ".yarn/cache/semver-npm-7.7.3-9cf7b3b46c-4afe5c9865.zip/node_modules/semver/ranges/valid.js"(exports2, module2) { + "use strict"; var Range3 = require_range(); var validRange = (range, options) => { try { @@ -2223,7 +2265,7 @@ var require_ms = __commonJS({ options = options || {}; var type = typeof val; if (type === "string" && val.length > 0) { - return parse5(val); + return parse4(val); } else if (type === "number" && isFinite(val)) { return options.long ? fmtLong(val) : fmtShort(val); } @@ -2231,7 +2273,7 @@ var require_ms = __commonJS({ "val is not a non-empty string or a valid number. val=" + JSON.stringify(val) ); }; - function parse5(str) { + function parse4(str) { str = String(str); if (str.length > 100) { return; @@ -2326,9 +2368,9 @@ var require_ms = __commonJS({ } }); -// .yarn/__virtual__/debug-virtual-f48feae4da/0/cache/debug-npm-4.4.0-f6efe76023-db94f1a182.zip/node_modules/debug/src/common.js +// .yarn/__virtual__/debug-virtual-436baa457e/0/cache/debug-npm-4.4.3-0105c6123a-d79136ec6c.zip/node_modules/debug/src/common.js var require_common = __commonJS({ - ".yarn/__virtual__/debug-virtual-f48feae4da/0/cache/debug-npm-4.4.0-f6efe76023-db94f1a182.zip/node_modules/debug/src/common.js"(exports2, module2) { + ".yarn/__virtual__/debug-virtual-436baa457e/0/cache/debug-npm-4.4.3-0105c6123a-d79136ec6c.zip/node_modules/debug/src/common.js"(exports2, module2) { function setup(env2) { createDebug.debug = createDebug; createDebug.default = createDebug; @@ -2429,7 +2471,7 @@ var require_common = __commonJS({ createDebug.namespaces = namespaces; createDebug.names = []; createDebug.skips = []; - const split = (typeof namespaces === "string" ? namespaces : "").trim().replace(" ", ",").split(",").filter(Boolean); + const split = (typeof namespaces === "string" ? namespaces : "").trim().replace(/\s+/g, ",").split(",").filter(Boolean); for (const ns of split) { if (ns[0] === "-") { createDebug.skips.push(ns.slice(1)); @@ -2503,9 +2545,9 @@ var require_common = __commonJS({ } }); -// .yarn/__virtual__/debug-virtual-f48feae4da/0/cache/debug-npm-4.4.0-f6efe76023-db94f1a182.zip/node_modules/debug/src/browser.js +// .yarn/__virtual__/debug-virtual-436baa457e/0/cache/debug-npm-4.4.3-0105c6123a-d79136ec6c.zip/node_modules/debug/src/browser.js var require_browser = __commonJS({ - ".yarn/__virtual__/debug-virtual-f48feae4da/0/cache/debug-npm-4.4.0-f6efe76023-db94f1a182.zip/node_modules/debug/src/browser.js"(exports2, module2) { + ".yarn/__virtual__/debug-virtual-436baa457e/0/cache/debug-npm-4.4.3-0105c6123a-d79136ec6c.zip/node_modules/debug/src/browser.js"(exports2, module2) { exports2.formatArgs = formatArgs; exports2.save = save; exports2.load = load; @@ -2647,7 +2689,7 @@ var require_browser = __commonJS({ function load() { let r; try { - r = exports2.storage.getItem("debug"); + r = exports2.storage.getItem("debug") || exports2.storage.getItem("DEBUG"); } catch (error) { } if (!r && typeof process !== "undefined" && "env" in process) { @@ -2673,7 +2715,7 @@ var require_browser = __commonJS({ } }); -// .yarn/cache/supports-color-npm-10.0.0-6cd1bb42a6-0e7884dfd0.zip/node_modules/supports-color/index.js +// .yarn/cache/supports-color-npm-10.2.2-e43ac15f9f-fb28dd7e0c.zip/node_modules/supports-color/index.js var supports_color_exports = {}; __export(supports_color_exports, { createSupportsColor: () => createSupportsColor, @@ -2767,11 +2809,17 @@ function _supportsColor(haveStream, { streamIsTTY, sniffFlags = true } = {}) { if (env.TERM === "xterm-kitty") { return 3; } + if (env.TERM === "xterm-ghostty") { + return 3; + } + if (env.TERM === "wezterm") { + return 3; + } if ("TERM_PROGRAM" in env) { - const version3 = Number.parseInt((env.TERM_PROGRAM_VERSION || "").split(".")[0], 10); + const version2 = Number.parseInt((env.TERM_PROGRAM_VERSION || "").split(".")[0], 10); switch (env.TERM_PROGRAM) { case "iTerm.app": { - return version3 >= 3 ? 3 : 2; + return version2 >= 3 ? 3 : 2; } case "Apple_Terminal": { return 2; @@ -2798,7 +2846,7 @@ function createSupportsColor(stream, options = {}) { } var import_node_process, import_node_os, import_node_tty, env, flagForceColor, supportsColor, supports_color_default; var init_supports_color = __esm({ - ".yarn/cache/supports-color-npm-10.0.0-6cd1bb42a6-0e7884dfd0.zip/node_modules/supports-color/index.js"() { + ".yarn/cache/supports-color-npm-10.2.2-e43ac15f9f-fb28dd7e0c.zip/node_modules/supports-color/index.js"() { import_node_process = __toESM(require("node:process"), 1); import_node_os = __toESM(require("node:os"), 1); import_node_tty = __toESM(require("node:tty"), 1); @@ -2816,9 +2864,9 @@ var init_supports_color = __esm({ } }); -// .yarn/__virtual__/debug-virtual-f48feae4da/0/cache/debug-npm-4.4.0-f6efe76023-db94f1a182.zip/node_modules/debug/src/node.js +// .yarn/__virtual__/debug-virtual-436baa457e/0/cache/debug-npm-4.4.3-0105c6123a-d79136ec6c.zip/node_modules/debug/src/node.js var require_node2 = __commonJS({ - ".yarn/__virtual__/debug-virtual-f48feae4da/0/cache/debug-npm-4.4.0-f6efe76023-db94f1a182.zip/node_modules/debug/src/node.js"(exports2, module2) { + ".yarn/__virtual__/debug-virtual-436baa457e/0/cache/debug-npm-4.4.3-0105c6123a-d79136ec6c.zip/node_modules/debug/src/node.js"(exports2, module2) { var tty2 = require("tty"); var util = require("util"); exports2.init = init; @@ -2990,9 +3038,9 @@ var require_node2 = __commonJS({ } }); -// .yarn/__virtual__/debug-virtual-f48feae4da/0/cache/debug-npm-4.4.0-f6efe76023-db94f1a182.zip/node_modules/debug/src/index.js +// .yarn/__virtual__/debug-virtual-436baa457e/0/cache/debug-npm-4.4.3-0105c6123a-d79136ec6c.zip/node_modules/debug/src/index.js var require_src = __commonJS({ - ".yarn/__virtual__/debug-virtual-f48feae4da/0/cache/debug-npm-4.4.0-f6efe76023-db94f1a182.zip/node_modules/debug/src/index.js"(exports2, module2) { + ".yarn/__virtual__/debug-virtual-436baa457e/0/cache/debug-npm-4.4.3-0105c6123a-d79136ec6c.zip/node_modules/debug/src/index.js"(exports2, module2) { if (typeof process === "undefined" || process.type === "renderer" || process.browser === true || process.__nwjs) { module2.exports = require_browser(); } else { @@ -3071,17 +3119,23 @@ var require_proxy_from_env = __commonJS({ } }); -// .yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/core/errors.js +// .yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/core/errors.js var require_errors = __commonJS({ - ".yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/core/errors.js"(exports2, module2) { + ".yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/core/errors.js"(exports2, module2) { "use strict"; + var kUndiciError = Symbol.for("undici.error.UND_ERR"); var UndiciError = class extends Error { constructor(message) { super(message); this.name = "UndiciError"; this.code = "UND_ERR"; } + static [Symbol.hasInstance](instance) { + return instance && instance[kUndiciError] === true; + } + [kUndiciError] = true; }; + var kConnectTimeoutError = Symbol.for("undici.error.UND_ERR_CONNECT_TIMEOUT"); var ConnectTimeoutError = class extends UndiciError { constructor(message) { super(message); @@ -3089,7 +3143,12 @@ var require_errors = __commonJS({ this.message = message || "Connect Timeout Error"; this.code = "UND_ERR_CONNECT_TIMEOUT"; } + static [Symbol.hasInstance](instance) { + return instance && instance[kConnectTimeoutError] === true; + } + [kConnectTimeoutError] = true; }; + var kHeadersTimeoutError = Symbol.for("undici.error.UND_ERR_HEADERS_TIMEOUT"); var HeadersTimeoutError = class extends UndiciError { constructor(message) { super(message); @@ -3097,7 +3156,12 @@ var require_errors = __commonJS({ this.message = message || "Headers Timeout Error"; this.code = "UND_ERR_HEADERS_TIMEOUT"; } + static [Symbol.hasInstance](instance) { + return instance && instance[kHeadersTimeoutError] === true; + } + [kHeadersTimeoutError] = true; }; + var kHeadersOverflowError = Symbol.for("undici.error.UND_ERR_HEADERS_OVERFLOW"); var HeadersOverflowError = class extends UndiciError { constructor(message) { super(message); @@ -3105,7 +3169,12 @@ var require_errors = __commonJS({ this.message = message || "Headers Overflow Error"; this.code = "UND_ERR_HEADERS_OVERFLOW"; } + static [Symbol.hasInstance](instance) { + return instance && instance[kHeadersOverflowError] === true; + } + [kHeadersOverflowError] = true; }; + var kBodyTimeoutError = Symbol.for("undici.error.UND_ERR_BODY_TIMEOUT"); var BodyTimeoutError = class extends UndiciError { constructor(message) { super(message); @@ -3113,7 +3182,12 @@ var require_errors = __commonJS({ this.message = message || "Body Timeout Error"; this.code = "UND_ERR_BODY_TIMEOUT"; } + static [Symbol.hasInstance](instance) { + return instance && instance[kBodyTimeoutError] === true; + } + [kBodyTimeoutError] = true; }; + var kResponseStatusCodeError = Symbol.for("undici.error.UND_ERR_RESPONSE_STATUS_CODE"); var ResponseStatusCodeError = class extends UndiciError { constructor(message, statusCode, headers, body) { super(message); @@ -3125,7 +3199,12 @@ var require_errors = __commonJS({ this.statusCode = statusCode; this.headers = headers; } + static [Symbol.hasInstance](instance) { + return instance && instance[kResponseStatusCodeError] === true; + } + [kResponseStatusCodeError] = true; }; + var kInvalidArgumentError = Symbol.for("undici.error.UND_ERR_INVALID_ARG"); var InvalidArgumentError = class extends UndiciError { constructor(message) { super(message); @@ -3133,7 +3212,12 @@ var require_errors = __commonJS({ this.message = message || "Invalid Argument Error"; this.code = "UND_ERR_INVALID_ARG"; } + static [Symbol.hasInstance](instance) { + return instance && instance[kInvalidArgumentError] === true; + } + [kInvalidArgumentError] = true; }; + var kInvalidReturnValueError = Symbol.for("undici.error.UND_ERR_INVALID_RETURN_VALUE"); var InvalidReturnValueError = class extends UndiciError { constructor(message) { super(message); @@ -3141,14 +3225,25 @@ var require_errors = __commonJS({ this.message = message || "Invalid Return Value Error"; this.code = "UND_ERR_INVALID_RETURN_VALUE"; } + static [Symbol.hasInstance](instance) { + return instance && instance[kInvalidReturnValueError] === true; + } + [kInvalidReturnValueError] = true; }; + var kAbortError = Symbol.for("undici.error.UND_ERR_ABORT"); var AbortError = class extends UndiciError { constructor(message) { super(message); this.name = "AbortError"; this.message = message || "The operation was aborted"; + this.code = "UND_ERR_ABORT"; } + static [Symbol.hasInstance](instance) { + return instance && instance[kAbortError] === true; + } + [kAbortError] = true; }; + var kRequestAbortedError = Symbol.for("undici.error.UND_ERR_ABORTED"); var RequestAbortedError = class extends AbortError { constructor(message) { super(message); @@ -3156,7 +3251,12 @@ var require_errors = __commonJS({ this.message = message || "Request aborted"; this.code = "UND_ERR_ABORTED"; } + static [Symbol.hasInstance](instance) { + return instance && instance[kRequestAbortedError] === true; + } + [kRequestAbortedError] = true; }; + var kInformationalError = Symbol.for("undici.error.UND_ERR_INFO"); var InformationalError = class extends UndiciError { constructor(message) { super(message); @@ -3164,7 +3264,12 @@ var require_errors = __commonJS({ this.message = message || "Request information"; this.code = "UND_ERR_INFO"; } + static [Symbol.hasInstance](instance) { + return instance && instance[kInformationalError] === true; + } + [kInformationalError] = true; }; + var kRequestContentLengthMismatchError = Symbol.for("undici.error.UND_ERR_REQ_CONTENT_LENGTH_MISMATCH"); var RequestContentLengthMismatchError = class extends UndiciError { constructor(message) { super(message); @@ -3172,7 +3277,12 @@ var require_errors = __commonJS({ this.message = message || "Request body length does not match content-length header"; this.code = "UND_ERR_REQ_CONTENT_LENGTH_MISMATCH"; } + static [Symbol.hasInstance](instance) { + return instance && instance[kRequestContentLengthMismatchError] === true; + } + [kRequestContentLengthMismatchError] = true; }; + var kResponseContentLengthMismatchError = Symbol.for("undici.error.UND_ERR_RES_CONTENT_LENGTH_MISMATCH"); var ResponseContentLengthMismatchError = class extends UndiciError { constructor(message) { super(message); @@ -3180,7 +3290,12 @@ var require_errors = __commonJS({ this.message = message || "Response body length does not match content-length header"; this.code = "UND_ERR_RES_CONTENT_LENGTH_MISMATCH"; } + static [Symbol.hasInstance](instance) { + return instance && instance[kResponseContentLengthMismatchError] === true; + } + [kResponseContentLengthMismatchError] = true; }; + var kClientDestroyedError = Symbol.for("undici.error.UND_ERR_DESTROYED"); var ClientDestroyedError = class extends UndiciError { constructor(message) { super(message); @@ -3188,7 +3303,12 @@ var require_errors = __commonJS({ this.message = message || "The client is destroyed"; this.code = "UND_ERR_DESTROYED"; } + static [Symbol.hasInstance](instance) { + return instance && instance[kClientDestroyedError] === true; + } + [kClientDestroyedError] = true; }; + var kClientClosedError = Symbol.for("undici.error.UND_ERR_CLOSED"); var ClientClosedError = class extends UndiciError { constructor(message) { super(message); @@ -3196,7 +3316,12 @@ var require_errors = __commonJS({ this.message = message || "The client is closed"; this.code = "UND_ERR_CLOSED"; } + static [Symbol.hasInstance](instance) { + return instance && instance[kClientClosedError] === true; + } + [kClientClosedError] = true; }; + var kSocketError = Symbol.for("undici.error.UND_ERR_SOCKET"); var SocketError = class extends UndiciError { constructor(message, socket) { super(message); @@ -3205,7 +3330,12 @@ var require_errors = __commonJS({ this.code = "UND_ERR_SOCKET"; this.socket = socket; } + static [Symbol.hasInstance](instance) { + return instance && instance[kSocketError] === true; + } + [kSocketError] = true; }; + var kNotSupportedError = Symbol.for("undici.error.UND_ERR_NOT_SUPPORTED"); var NotSupportedError = class extends UndiciError { constructor(message) { super(message); @@ -3213,7 +3343,12 @@ var require_errors = __commonJS({ this.message = message || "Not supported error"; this.code = "UND_ERR_NOT_SUPPORTED"; } + static [Symbol.hasInstance](instance) { + return instance && instance[kNotSupportedError] === true; + } + [kNotSupportedError] = true; }; + var kBalancedPoolMissingUpstreamError = Symbol.for("undici.error.UND_ERR_BPL_MISSING_UPSTREAM"); var BalancedPoolMissingUpstreamError = class extends UndiciError { constructor(message) { super(message); @@ -3221,7 +3356,12 @@ var require_errors = __commonJS({ this.message = message || "No upstream has been added to the BalancedPool"; this.code = "UND_ERR_BPL_MISSING_UPSTREAM"; } + static [Symbol.hasInstance](instance) { + return instance && instance[kBalancedPoolMissingUpstreamError] === true; + } + [kBalancedPoolMissingUpstreamError] = true; }; + var kHTTPParserError = Symbol.for("undici.error.UND_ERR_HTTP_PARSER"); var HTTPParserError = class extends Error { constructor(message, code2, data) { super(message); @@ -3229,7 +3369,12 @@ var require_errors = __commonJS({ this.code = code2 ? `HPE_${code2}` : void 0; this.data = data ? data.toString() : void 0; } + static [Symbol.hasInstance](instance) { + return instance && instance[kHTTPParserError] === true; + } + [kHTTPParserError] = true; }; + var kResponseExceededMaxSizeError = Symbol.for("undici.error.UND_ERR_RES_EXCEEDED_MAX_SIZE"); var ResponseExceededMaxSizeError = class extends UndiciError { constructor(message) { super(message); @@ -3237,7 +3382,12 @@ var require_errors = __commonJS({ this.message = message || "Response content exceeded max size"; this.code = "UND_ERR_RES_EXCEEDED_MAX_SIZE"; } + static [Symbol.hasInstance](instance) { + return instance && instance[kResponseExceededMaxSizeError] === true; + } + [kResponseExceededMaxSizeError] = true; }; + var kRequestRetryError = Symbol.for("undici.error.UND_ERR_REQ_RETRY"); var RequestRetryError = class extends UndiciError { constructor(message, code2, { headers, data }) { super(message); @@ -3248,7 +3398,12 @@ var require_errors = __commonJS({ this.data = data; this.headers = headers; } + static [Symbol.hasInstance](instance) { + return instance && instance[kRequestRetryError] === true; + } + [kRequestRetryError] = true; }; + var kResponseError = Symbol.for("undici.error.UND_ERR_RESPONSE"); var ResponseError = class extends UndiciError { constructor(message, code2, { headers, data }) { super(message); @@ -3259,7 +3414,12 @@ var require_errors = __commonJS({ this.data = data; this.headers = headers; } + static [Symbol.hasInstance](instance) { + return instance && instance[kResponseError] === true; + } + [kResponseError] = true; }; + var kSecureProxyConnectionError = Symbol.for("undici.error.UND_ERR_PRX_TLS"); var SecureProxyConnectionError = class extends UndiciError { constructor(cause, message, options) { super(message, { cause, ...options ?? {} }); @@ -3268,6 +3428,10 @@ var require_errors = __commonJS({ this.code = "UND_ERR_PRX_TLS"; this.cause = cause; } + static [Symbol.hasInstance](instance) { + return instance && instance[kSecureProxyConnectionError] === true; + } + [kSecureProxyConnectionError] = true; }; module2.exports = { AbortError, @@ -3297,9 +3461,9 @@ var require_errors = __commonJS({ } }); -// .yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/core/symbols.js +// .yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/core/symbols.js var require_symbols = __commonJS({ - ".yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/core/symbols.js"(exports2, module2) { + ".yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/core/symbols.js"(exports2, module2) { module2.exports = { kClose: Symbol("close"), kDestroy: Symbol("destroy"), @@ -3370,9 +3534,9 @@ var require_symbols = __commonJS({ } }); -// .yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/core/constants.js +// .yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/core/constants.js var require_constants2 = __commonJS({ - ".yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/core/constants.js"(exports2, module2) { + ".yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/core/constants.js"(exports2, module2) { "use strict"; var headerNameLowerCasedRecord = {}; var wellknownHeaderNames = [ @@ -3485,9 +3649,9 @@ var require_constants2 = __commonJS({ } }); -// .yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/core/tree.js +// .yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/core/tree.js var require_tree = __commonJS({ - ".yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/core/tree.js"(exports2, module2) { + ".yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/core/tree.js"(exports2, module2) { "use strict"; var { wellknownHeaderNames, @@ -3625,9 +3789,9 @@ var require_tree = __commonJS({ } }); -// .yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/core/util.js +// .yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/core/util.js var require_util = __commonJS({ - ".yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/core/util.js"(exports2, module2) { + ".yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/core/util.js"(exports2, module2) { "use strict"; var assert5 = require("node:assert"); var { kDestroyed, kBodyUsed, kListeners, kBody } = require_symbols(); @@ -4131,9 +4295,9 @@ var require_util = __commonJS({ } }); -// .yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/api/readable.js +// .yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/api/readable.js var require_readable = __commonJS({ - ".yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/api/readable.js"(exports2, module2) { + ".yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/api/readable.js"(exports2, module2) { "use strict"; var assert5 = require("node:assert"); var { Readable: Readable2 } = require("node:stream"); @@ -4146,7 +4310,7 @@ var require_readable = __commonJS({ var kAbort = Symbol("kAbort"); var kContentType = Symbol("kContentType"); var kContentLength = Symbol("kContentLength"); - var noop2 = () => { + var noop3 = () => { }; var BodyReadable = class extends Readable2 { constructor({ @@ -4263,7 +4427,7 @@ var require_readable = __commonJS({ if (this._readableState.closeEmitted) { return null; } - return await new Promise((resolve2, reject) => { + return await new Promise((resolve, reject) => { if (this[kContentLength] > limit) { this.destroy(new AbortError()); } @@ -4276,9 +4440,9 @@ var require_readable = __commonJS({ if (signal?.aborted) { reject(signal.reason ?? new AbortError()); } else { - resolve2(null); + resolve(null); } - }).on("error", noop2).on("data", function(chunk) { + }).on("error", noop3).on("data", function(chunk) { limit -= chunk.length; if (limit <= 0) { this.destroy(); @@ -4295,7 +4459,7 @@ var require_readable = __commonJS({ } async function consume(stream, type) { assert5(!stream[kConsume]); - return new Promise((resolve2, reject) => { + return new Promise((resolve, reject) => { if (isUnusable(stream)) { const rState = stream._readableState; if (rState.destroyed && rState.closeEmitted === false) { @@ -4312,7 +4476,7 @@ var require_readable = __commonJS({ stream[kConsume] = { type, stream, - resolve: resolve2, + resolve, reject, length: 0, body: [] @@ -4382,18 +4546,18 @@ var require_readable = __commonJS({ return buffer; } function consumeEnd(consume2) { - const { type, body, resolve: resolve2, stream, length } = consume2; + const { type, body, resolve, stream, length } = consume2; try { if (type === "text") { - resolve2(chunksDecode(body, length)); + resolve(chunksDecode(body, length)); } else if (type === "json") { - resolve2(JSON.parse(chunksDecode(body, length))); + resolve(JSON.parse(chunksDecode(body, length))); } else if (type === "arrayBuffer") { - resolve2(chunksConcat(body, length).buffer); + resolve(chunksConcat(body, length).buffer); } else if (type === "blob") { - resolve2(new Blob(body, { type: stream[kContentType] })); + resolve(new Blob(body, { type: stream[kContentType] })); } else if (type === "bytes") { - resolve2(chunksConcat(body, length)); + resolve(chunksConcat(body, length)); } consumeFinish(consume2); } catch (err) { @@ -4424,9 +4588,9 @@ var require_readable = __commonJS({ } }); -// .yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/api/util.js +// .yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/api/util.js var require_util2 = __commonJS({ - ".yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/api/util.js"(exports2, module2) { + ".yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/api/util.js"(exports2, module2) { var assert5 = require("node:assert"); var { ResponseStatusCodeError @@ -4485,9 +4649,9 @@ var require_util2 = __commonJS({ } }); -// .yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/api/api-request.js +// .yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/api/api-request.js var require_api_request = __commonJS({ - ".yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/api/api-request.js"(exports2, module2) { + ".yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/api/api-request.js"(exports2, module2) { "use strict"; var assert5 = require("node:assert"); var { Readable: Readable2 } = require_readable(); @@ -4650,9 +4814,9 @@ var require_api_request = __commonJS({ }; function request(opts, callback) { if (callback === void 0) { - return new Promise((resolve2, reject) => { + return new Promise((resolve, reject) => { request.call(this, opts, (err, data) => { - return err ? reject(err) : resolve2(data); + return err ? reject(err) : resolve(data); }); }); } @@ -4671,9 +4835,9 @@ var require_api_request = __commonJS({ } }); -// .yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/api/abort-signal.js +// .yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/api/abort-signal.js var require_abort_signal = __commonJS({ - ".yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/api/abort-signal.js"(exports2, module2) { + ".yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/api/abort-signal.js"(exports2, module2) { var { addAbortListener } = require_util(); var { RequestAbortedError } = require_errors(); var kListener = Symbol("kListener"); @@ -4722,9 +4886,9 @@ var require_abort_signal = __commonJS({ } }); -// .yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/api/api-stream.js +// .yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/api/api-stream.js var require_api_stream = __commonJS({ - ".yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/api/api-stream.js"(exports2, module2) { + ".yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/api/api-stream.js"(exports2, module2) { "use strict"; var assert5 = require("node:assert"); var { finished, PassThrough } = require("node:stream"); @@ -4875,9 +5039,9 @@ var require_api_stream = __commonJS({ }; function stream(opts, factory, callback) { if (callback === void 0) { - return new Promise((resolve2, reject) => { + return new Promise((resolve, reject) => { stream.call(this, opts, factory, (err, data) => { - return err ? reject(err) : resolve2(data); + return err ? reject(err) : resolve(data); }); }); } @@ -4895,9 +5059,9 @@ var require_api_stream = __commonJS({ } }); -// .yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/api/api-pipeline.js +// .yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/api/api-pipeline.js var require_api_pipeline = __commonJS({ - ".yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/api/api-pipeline.js"(exports2, module2) { + ".yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/api/api-pipeline.js"(exports2, module2) { "use strict"; var { Readable: Readable2, @@ -5095,9 +5259,9 @@ var require_api_pipeline = __commonJS({ } }); -// .yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/api/api-upgrade.js +// .yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/api/api-upgrade.js var require_api_upgrade = __commonJS({ - ".yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/api/api-upgrade.js"(exports2, module2) { + ".yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/api/api-upgrade.js"(exports2, module2) { "use strict"; var { InvalidArgumentError, SocketError } = require_errors(); var { AsyncResource } = require("node:async_hooks"); @@ -5162,9 +5326,9 @@ var require_api_upgrade = __commonJS({ }; function upgrade(opts, callback) { if (callback === void 0) { - return new Promise((resolve2, reject) => { + return new Promise((resolve, reject) => { upgrade.call(this, opts, (err, data) => { - return err ? reject(err) : resolve2(data); + return err ? reject(err) : resolve(data); }); }); } @@ -5187,9 +5351,9 @@ var require_api_upgrade = __commonJS({ } }); -// .yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/api/api-connect.js +// .yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/api/api-connect.js var require_api_connect = __commonJS({ - ".yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/api/api-connect.js"(exports2, module2) { + ".yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/api/api-connect.js"(exports2, module2) { "use strict"; var assert5 = require("node:assert"); var { AsyncResource } = require("node:async_hooks"); @@ -5256,9 +5420,9 @@ var require_api_connect = __commonJS({ }; function connect(opts, callback) { if (callback === void 0) { - return new Promise((resolve2, reject) => { + return new Promise((resolve, reject) => { connect.call(this, opts, (err, data) => { - return err ? reject(err) : resolve2(data); + return err ? reject(err) : resolve(data); }); }); } @@ -5277,9 +5441,9 @@ var require_api_connect = __commonJS({ } }); -// .yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/api/index.js +// .yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/api/index.js var require_api = __commonJS({ - ".yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/api/index.js"(exports2, module2) { + ".yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/api/index.js"(exports2, module2) { "use strict"; module2.exports.request = require_api_request(); module2.exports.stream = require_api_stream(); @@ -5289,9 +5453,9 @@ var require_api = __commonJS({ } }); -// .yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/dispatcher/dispatcher.js +// .yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/dispatcher/dispatcher.js var require_dispatcher = __commonJS({ - ".yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/dispatcher/dispatcher.js"(exports2, module2) { + ".yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/dispatcher/dispatcher.js"(exports2, module2) { "use strict"; var EventEmitter2 = require("node:events"); var Dispatcher = class extends EventEmitter2 { @@ -5344,9 +5508,9 @@ var require_dispatcher = __commonJS({ } }); -// .yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/dispatcher/dispatcher-base.js +// .yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/dispatcher/dispatcher-base.js var require_dispatcher_base = __commonJS({ - ".yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/dispatcher/dispatcher-base.js"(exports2, module2) { + ".yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/dispatcher/dispatcher-base.js"(exports2, module2) { "use strict"; var Dispatcher = require_dispatcher(); var { @@ -5388,9 +5552,9 @@ var require_dispatcher_base = __commonJS({ } close(callback) { if (callback === void 0) { - return new Promise((resolve2, reject) => { + return new Promise((resolve, reject) => { this.close((err, data) => { - return err ? reject(err) : resolve2(data); + return err ? reject(err) : resolve(data); }); }); } @@ -5428,12 +5592,12 @@ var require_dispatcher_base = __commonJS({ err = null; } if (callback === void 0) { - return new Promise((resolve2, reject) => { + return new Promise((resolve, reject) => { this.destroy(err, (err2, data) => { return err2 ? ( /* istanbul ignore next: should never error */ reject(err2) - ) : resolve2(data); + ) : resolve(data); }); }); } @@ -5505,9 +5669,9 @@ var require_dispatcher_base = __commonJS({ } }); -// .yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/dispatcher/fixed-queue.js +// .yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/dispatcher/fixed-queue.js var require_fixed_queue = __commonJS({ - ".yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/dispatcher/fixed-queue.js"(exports2, module2) { + ".yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/dispatcher/fixed-queue.js"(exports2, module2) { "use strict"; var kSize = 2048; var kMask = kSize - 1; @@ -5562,9 +5726,9 @@ var require_fixed_queue = __commonJS({ } }); -// .yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/dispatcher/pool-stats.js +// .yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/dispatcher/pool-stats.js var require_pool_stats = __commonJS({ - ".yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/dispatcher/pool-stats.js"(exports2, module2) { + ".yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/dispatcher/pool-stats.js"(exports2, module2) { var { kFree, kConnected, kPending, kQueued, kRunning, kSize } = require_symbols(); var kPool = Symbol("pool"); var PoolStats = class { @@ -5594,9 +5758,9 @@ var require_pool_stats = __commonJS({ } }); -// .yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/dispatcher/pool-base.js +// .yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/dispatcher/pool-base.js var require_pool_base = __commonJS({ - ".yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/dispatcher/pool-base.js"(exports2, module2) { + ".yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/dispatcher/pool-base.js"(exports2, module2) { "use strict"; var DispatcherBase = require_dispatcher_base(); var FixedQueue = require_fixed_queue(); @@ -5689,8 +5853,8 @@ var require_pool_base = __commonJS({ if (this[kQueue].isEmpty()) { await Promise.all(this[kClients].map((c) => c.close())); } else { - await new Promise((resolve2) => { - this[kClosedResolve] = resolve2; + await new Promise((resolve) => { + this[kClosedResolve] = resolve; }); } } @@ -5749,9 +5913,9 @@ var require_pool_base = __commonJS({ } }); -// .yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/core/diagnostics.js +// .yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/core/diagnostics.js var require_diagnostics = __commonJS({ - ".yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/core/diagnostics.js"(exports2, module2) { + ".yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/core/diagnostics.js"(exports2, module2) { "use strict"; var diagnosticsChannel = require("node:diagnostics_channel"); var util = require("node:util"); @@ -5782,36 +5946,36 @@ var require_diagnostics = __commonJS({ const debuglog = fetchDebuglog.enabled ? fetchDebuglog : undiciDebugLog; diagnosticsChannel.channel("undici:client:beforeConnect").subscribe((evt) => { const { - connectParams: { version: version3, protocol, port, host } + connectParams: { version: version2, protocol, port, host } } = evt; debuglog( "connecting to %s using %s%s", `${host}${port ? `:${port}` : ""}`, protocol, - version3 + version2 ); }); diagnosticsChannel.channel("undici:client:connected").subscribe((evt) => { const { - connectParams: { version: version3, protocol, port, host } + connectParams: { version: version2, protocol, port, host } } = evt; debuglog( "connected to %s using %s%s", `${host}${port ? `:${port}` : ""}`, protocol, - version3 + version2 ); }); diagnosticsChannel.channel("undici:client:connectError").subscribe((evt) => { const { - connectParams: { version: version3, protocol, port, host }, + connectParams: { version: version2, protocol, port, host }, error } = evt; debuglog( "connection to %s using %s%s errored - %s", `${host}${port ? `:${port}` : ""}`, protocol, - version3, + version2, error.message ); }); @@ -5860,31 +6024,31 @@ var require_diagnostics = __commonJS({ const debuglog = undiciDebugLog.enabled ? undiciDebugLog : websocketDebuglog; diagnosticsChannel.channel("undici:client:beforeConnect").subscribe((evt) => { const { - connectParams: { version: version3, protocol, port, host } + connectParams: { version: version2, protocol, port, host } } = evt; debuglog( "connecting to %s%s using %s%s", host, port ? `:${port}` : "", protocol, - version3 + version2 ); }); diagnosticsChannel.channel("undici:client:connected").subscribe((evt) => { const { - connectParams: { version: version3, protocol, port, host } + connectParams: { version: version2, protocol, port, host } } = evt; debuglog( "connected to %s%s using %s%s", host, port ? `:${port}` : "", protocol, - version3 + version2 ); }); diagnosticsChannel.channel("undici:client:connectError").subscribe((evt) => { const { - connectParams: { version: version3, protocol, port, host }, + connectParams: { version: version2, protocol, port, host }, error } = evt; debuglog( @@ -5892,7 +6056,7 @@ var require_diagnostics = __commonJS({ host, port ? `:${port}` : "", protocol, - version3, + version2, error.message ); }); @@ -5934,9 +6098,9 @@ var require_diagnostics = __commonJS({ } }); -// .yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/core/request.js +// .yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/core/request.js var require_request = __commonJS({ - ".yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/core/request.js"(exports2, module2) { + ".yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/core/request.js"(exports2, module2) { "use strict"; var { InvalidArgumentError, @@ -6258,9 +6422,9 @@ var require_request = __commonJS({ } }); -// .yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/util/timers.js +// .yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/util/timers.js var require_timers = __commonJS({ - ".yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/util/timers.js"(exports2, module2) { + ".yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/util/timers.js"(exports2, module2) { "use strict"; var fastNow = 0; var RESOLUTION_MS = 1e3; @@ -6489,16 +6653,16 @@ var require_timers = __commonJS({ } }); -// .yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/core/connect.js +// .yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/core/connect.js var require_connect = __commonJS({ - ".yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/core/connect.js"(exports2, module2) { + ".yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/core/connect.js"(exports2, module2) { "use strict"; var net = require("node:net"); var assert5 = require("node:assert"); var util = require_util(); var { InvalidArgumentError, ConnectTimeoutError } = require_errors(); var timers = require_timers(); - function noop2() { + function noop3() { } var tls; var SessionCache; @@ -6623,7 +6787,7 @@ var require_connect = __commonJS({ } var setupConnectTimeout = process.platform === "win32" ? (socketWeakRef, opts) => { if (!opts.timeout) { - return noop2; + return noop3; } let s1 = null; let s2 = null; @@ -6639,7 +6803,7 @@ var require_connect = __commonJS({ }; } : (socketWeakRef, opts) => { if (!opts.timeout) { - return noop2; + return noop3; } let s1 = null; const fastTimer = timers.setFastTimeout(() => { @@ -6669,9 +6833,9 @@ var require_connect = __commonJS({ } }); -// .yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/llhttp/utils.js +// .yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/llhttp/utils.js var require_utils = __commonJS({ - ".yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/llhttp/utils.js"(exports2) { + ".yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/llhttp/utils.js"(exports2) { "use strict"; Object.defineProperty(exports2, "__esModule", { value: true }); exports2.enumToMap = void 0; @@ -6689,9 +6853,9 @@ var require_utils = __commonJS({ } }); -// .yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/llhttp/constants.js +// .yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/llhttp/constants.js var require_constants3 = __commonJS({ - ".yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/llhttp/constants.js"(exports2) { + ".yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/llhttp/constants.js"(exports2) { "use strict"; Object.defineProperty(exports2, "__esModule", { value: true }); exports2.SPECIAL_HEADERS = exports2.HEADER_STATE = exports2.MINOR = exports2.MAJOR = exports2.CONNECTION_TOKEN_CHARS = exports2.HEADER_CHARS = exports2.TOKEN = exports2.STRICT_TOKEN = exports2.HEX = exports2.URL_CHAR = exports2.STRICT_URL_CHAR = exports2.USERINFO_CHARS = exports2.MARK = exports2.ALPHANUM = exports2.NUM = exports2.HEX_MAP = exports2.NUM_MAP = exports2.ALPHA = exports2.FINISH = exports2.H_METHOD_MAP = exports2.METHOD_MAP = exports2.METHODS_RTSP = exports2.METHODS_ICE = exports2.METHODS_HTTP = exports2.METHODS = exports2.LENIENT_FLAGS = exports2.FLAGS = exports2.TYPE = exports2.ERROR = void 0; @@ -7010,27 +7174,27 @@ var require_constants3 = __commonJS({ } }); -// .yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/llhttp/llhttp-wasm.js +// .yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/llhttp/llhttp-wasm.js var require_llhttp_wasm = __commonJS({ - ".yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/llhttp/llhttp-wasm.js"(exports2, module2) { + ".yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/llhttp/llhttp-wasm.js"(exports2, module2) { "use strict"; var { Buffer: Buffer3 } = require("node:buffer"); module2.exports = Buffer3.from("AGFzbQEAAAABJwdgAX8Bf2ADf39/AX9gAX8AYAJ/fwBgBH9/f38Bf2AAAGADf39/AALLAQgDZW52GHdhc21fb25faGVhZGVyc19jb21wbGV0ZQAEA2VudhV3YXNtX29uX21lc3NhZ2VfYmVnaW4AAANlbnYLd2FzbV9vbl91cmwAAQNlbnYOd2FzbV9vbl9zdGF0dXMAAQNlbnYUd2FzbV9vbl9oZWFkZXJfZmllbGQAAQNlbnYUd2FzbV9vbl9oZWFkZXJfdmFsdWUAAQNlbnYMd2FzbV9vbl9ib2R5AAEDZW52GHdhc21fb25fbWVzc2FnZV9jb21wbGV0ZQAAAy0sBQYAAAIAAAAAAAACAQIAAgICAAADAAAAAAMDAwMBAQEBAQEBAQEAAAIAAAAEBQFwARISBQMBAAIGCAF/AUGA1AQLB9EFIgZtZW1vcnkCAAtfaW5pdGlhbGl6ZQAIGV9faW5kaXJlY3RfZnVuY3Rpb25fdGFibGUBAAtsbGh0dHBfaW5pdAAJGGxsaHR0cF9zaG91bGRfa2VlcF9hbGl2ZQAvDGxsaHR0cF9hbGxvYwALBm1hbGxvYwAxC2xsaHR0cF9mcmVlAAwEZnJlZQAMD2xsaHR0cF9nZXRfdHlwZQANFWxsaHR0cF9nZXRfaHR0cF9tYWpvcgAOFWxsaHR0cF9nZXRfaHR0cF9taW5vcgAPEWxsaHR0cF9nZXRfbWV0aG9kABAWbGxodHRwX2dldF9zdGF0dXNfY29kZQAREmxsaHR0cF9nZXRfdXBncmFkZQASDGxsaHR0cF9yZXNldAATDmxsaHR0cF9leGVjdXRlABQUbGxodHRwX3NldHRpbmdzX2luaXQAFQ1sbGh0dHBfZmluaXNoABYMbGxodHRwX3BhdXNlABcNbGxodHRwX3Jlc3VtZQAYG2xsaHR0cF9yZXN1bWVfYWZ0ZXJfdXBncmFkZQAZEGxsaHR0cF9nZXRfZXJybm8AGhdsbGh0dHBfZ2V0X2Vycm9yX3JlYXNvbgAbF2xsaHR0cF9zZXRfZXJyb3JfcmVhc29uABwUbGxodHRwX2dldF9lcnJvcl9wb3MAHRFsbGh0dHBfZXJybm9fbmFtZQAeEmxsaHR0cF9tZXRob2RfbmFtZQAfEmxsaHR0cF9zdGF0dXNfbmFtZQAgGmxsaHR0cF9zZXRfbGVu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PgtBhwEMPQtBiAEMPAtBiQEMOwtBigEMOgtBiwEMOQtBjAEMOAtBjQEMNwtBjgEMNgtBjwEMNQtBkAEMNAtBkQEMMwtBkgEMMgtBkwEMMQtBlAEMMAtBlQEMLwtBlgEMLgtBlwEMLQtBmAEMLAtBmQEMKwtBmgEMKgtBmwEMKQtBnAEMKAtBnQEMJwtBngEMJgtBnwEMJQtBoAEMJAtBoQEMIwtBogEMIgtBowEMIQtBpAEMIAtBpQEMHwtBpgEMHgtBpwEMHQtBqAEMHAtBqQEMGwtBqgEMGgtBqwEMGQtBrAEMGAtBrQEMFwtBrgEMFgtBAQwVC0GvAQwUC0GwAQwTC0GxAQwSC0GzAQwRC0GyAQwQC0G0AQwPC0G1AQwOC0G2AQwNC0G3AQwMC0G4AQwLC0G5AQwKC0G6AQwJC0G7AQwIC0HGAQwHC0G8AQwGC0G9AQwFC0G+AQwEC0G/AQwDC0HAAQwCC0HCAQwBC0HBAQshAwNAAkACQAJAAkACQAJAAkACQAJAIAICfwJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJ/AkACQAJAAkACQAJAAkACQAJAAkACQAJAAkAgAgJ/AkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACfwJAAkACfwJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkAC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iQEhAwykAgtBASABLQAAQd8ARw1nGiABQQFqIQEMXgtBigEhAyABIARGDaICIAIoAgAiACAEIAFraiEFIAEgAGtBAWohBgNAIAEtAAAgAEGMzwBqLQAARw1kIABBAUYN+gEgAEEBaiEAIAQgAUEBaiIBRw0ACyACIAU2AgAMogILQYsBIQMgASAERg2hAiACKAIAIgAgBCABa2ohBSABIABrQQJqIQYCQANAIAEtAAAgAEGOzwBqLQAARw1kIABBAkYNASAAQQFqIQAgBCABQQFqIgFHDQALIAIgBTYCAAyiAgsgAkEANgIAIAZBAWohAUECDGULQYwBIQMgASAERg2gAiACKAIAIgAgBCABa2ohBSABIABrQQFqIQYCQANAIAEtAAAgAEHwzwBqLQAARw1jIABBAUYNASAAQQFqIQAgBCABQQFqIgFHDQALIAIgBTYCAAyhAgsgAkEANgIAIAZBAWohAUEfDGQLQY0BIQMgASAERg2fAiACKAIAIgAgBCABa2ohBSABIABrQQFqIQYCQANAIAEtAAAgAEHyzwBqLQAARw1iIABBAUYNASAAQQFqIQAgBCABQQFqIgFHDQALIAIgBTYCAAygAgsgAkEANgIAIAZBAWohAUEJDGMLIAEgBEYEQEGOASEDDJ8CCwJAAkAgAS0AAEHJAGsOBwBiYmJiYgFiCyABQQFqIQFB+AAhAwyGAgsgAUEBaiEBQfkAIQMMhQILQY8BIQMgASAERg2dAiACKAIAIgAgBCABa2ohBSABIABrQQVqIQYCQANAIAEtAAAgAEGRzwBqLQAARw1gIABBBUYNASAAQQFqIQAgBCABQQFqIgFHDQALIAIgBTYCAAyeAgsgAkEANgIAIAZBAWohAUEYDGELQZABIQMgASAERg2cAiACKAIAIgAgBCABa2ohBSABIABrQQJqIQYCQANAIAEtAAAgAEGXzwBqLQAARw1fIABBAkYNASAAQQFqIQAgBCABQQFqIgFHDQALIAIgBTYCAAydAgsgAkEANgIAIAZBAWohAUEXDGALQZEBIQMgASAERg2bAiACKAIAIgAgBCABa2ohBSABIABrQQZqIQYCQANAIAEtAAAgAEGazwBqLQAARw1eIABBBkYNASAAQQFqIQAgBCABQQFqIgFHDQALIAIgBTYCAAycAgsgAkEANgIAIAZBAWohAUEV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JSIARQ0RIAJB5QA2AhwgAiABNgIUIAIgADYCDEEAIQMMtAELIAIoAgQhACACQQA2AgQgAiAAIAEQJSIARQ0gIAJB0wA2AhwgAiABNgIUIAIgADYCDEEAIQMMswELIAIoAgQhACACQQA2AgQgAiAAIAEQJSIARQ0iIAJB0gA2AhwgAiABNgIUIAIgADYCDEEAIQMMsgELIAIoAgQhACACQQA2AgQgAiAAIAEQJSIARQ0OIAJB5QA2AhwgAiABNgIUIAIgADYCDEEAIQMMsQELIAIoAgQhACACQQA2AgQgAiAAIAEQJSIARQ0dIAJB0wA2AhwgAiABNgIUIAIgADYCDEEAIQMMsAELIAIoAgQhACACQQA2AgQgAiAAIAEQJSIARQ0fIAJB0gA2AhwgAiABNgIUIAIgADYCDEEAIQMMrwELIABBP0cNASABQQFqCyEBQQUhAwyUAQtBACEDIAJBADYCHCACIAE2AhQgAkH9EjYCECACQQc2AgwMrAELIAJBADYCHCACIAE2AhQgAkHcCDYCECACQQc2AgxBACEDDKsBCyACKAIEIQAgAkEANgIEIAIgACABECUiAEUNByACQeUANgIcIAIgATYCFCACIAA2AgxBACEDDKoBCyACKAIEIQAgAkEANgIEIAIgACABECUiAEUNFiACQdMANgIcIAIgATYCFCACIAA2AgxBACEDDKkBCyACKAIEIQAgAkEANgIEIAIgACABECUiAEUNGCACQdIANgIcIAIgATYCFCACIAA2AgxBACEDDKgBCyACQQA2AhwgAiABNgIUIAJBxgo2AhAgAkEHNgIMQQAhAwynAQsgAigCBCEAIAJBADYCBCACIAAgARAlIgBFDQMgAkHlADYCHCACIAE2AhQgAiAANgIMQQAhAwymAQsgAigCBCEAIAJBADYCBCACIAAgARAlIgBFDRIgAkHTADYCHCACIAE2AhQgAiAANgIMQQAhAwylAQsgAigCBCEAIAJBADYCBCACIAAgARAlIgBFDRQgAkHSADYCHCACIAE2AhQgAiAANgIMQQAhAwykAQsgAigCBCEAIAJBADYCBCACIAAgARAlIgBFDQAgAkHlADYCHCACIAE2AhQgAiAANgIMQQAhAwyjAQtB1QAhAwyJAQsgAEEVRwRAIAJBADYCHCACIAE2AhQgAkG5DTYCECACQRo2AgxB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AkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAIABB5ABrDvQDY2IAAWFhYWFhYQIDBAVhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhBgcICQoLDA0OD2FhYWFhEGFhYWFhYWFhYWFhEWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYRITFBUWFxgZGhthYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhHB0eHyAhIiMkJSYnKCkqKywtLi8wMTIzNDU2YTc4OTphYWFhYWFhYTthYWE8YWFhYT0+P2FhYWFhYWFhQGFhQWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYUJDREVGR0hJSktMTU5PUFFSU2FhYWFhYWFhVFVWV1hZWlthXF1hYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFeYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhX2BhC0HhJw8LQaQhDwtByywPC0H+MQ8LQcAkDwtBqyQPC0GNKA8LQeImDwtBgDAPC0G5Lw8LQdckDwtB7x8PC0HhHw8LQfofDwtB8iAPC0GoLw8LQa4yDwtBiDAPC0HsJw8LQYIiDwtBjh0PC0HQLg8LQcojDwtBxTIPC0HfHA8LQdIcDwtBxCAPC0HXIA8LQaIfDwtB7S4PC0GrMA8LQdQlDwtBzC4PC0H6Lg8LQfwrDwtB0jAPC0HxHQ8LQbsgDwtB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CABB3AgLii1JbnZhbGlkIGNoYXIgaW4gdXJsIHF1ZXJ5AFNwYW4gY2FsbGJhY2sgZXJyb3IgaW4gb25fYm9keQBDb250ZW50LUxlbmd0aCBvdmVyZmxvdwBDaHVuayBzaXplIG92ZXJmbG93AFJlc3BvbnNlIG92ZXJmbG93AEludmFsaWQgbWV0aG9kIGZvciBIVFRQL3gueCByZXF1ZXN0AEludmFsaWQgbWV0aG9kIGZvciBSVFNQL3gueCByZXF1ZXN0AEV4cGVjdGVkIFNPVVJDRSBtZXRob2QgZm9yIElDRS94LnggcmVxdWVzdABJbnZhbGlkIGNoYXIgaW4gdXJsIGZyYWdtZW50IHN0YXJ0AEV4cGVjdGVkIGRvdABTcGFuIGNhbGxiYWNrIGVycm9yIGluIG9uX3N0YXR1cwBJbnZhbGlkIHJlc3BvbnNlIHN0YXR1cwBJbnZhbGlkIGNoYXJhY3RlciBpbiBjaHVuayBleHRlbnNpb25zAFVzZXIgY2FsbGJhY2sgZXJyb3IAYG9uX3Jlc2V0YCBjYWxsYmFjayBlcnJvcgBgb25fY2h1bmtfaGVhZGVyYCBjYWxsYmFjayBlcnJvcgBgb25fbWVzc2FnZV9iZWdpbmAgY2FsbGJhY2sgZXJyb3IAYG9uX2NodW5rX2V4dGVuc2lvbl92YWx1ZWAgY2FsbGJhY2sgZXJyb3IAYG9uX3N0YXR1c19jb21wbGV0ZWAgY2FsbGJhY2sgZXJyb3IAYG9uX3ZlcnNpb25fY29tcGxldGVgIGNhbGxiYWNrIGVycm9yAGBvbl91cmxfY29tcGxldGVgIGNhbGxiYWNrIGVycm9yAGBvbl9jaHVua19jb21wbGV0ZWAgY2FsbGJhY2sgZXJyb3IAYG9uX2hlYWRlcl92YWx1ZV9jb21wbGV0ZWAgY2FsbGJhY2sgZXJyb3IAYG9uX21lc3NhZ2VfY29tcGxldGVgIGNhbGxiYWNrIGVycm9yAGBvbl9tZXRob2RfY29tcGxldGVgIGNhbGxiYWNrIGVycm9yAGBvbl9oZWFkZXJfZmllbGRfY29tcGxldGVgIGNhbGxiYWNrIGVycm9yAGBvbl9jaHVua19leHRlbnNpb25fbmFtZWAgY2FsbGJhY2sgZXJyb3IAVW5leHBlY3RlZCBjaGFyIGluIHVybCBzZXJ2ZXIASW52YWxpZCBoZWFkZXIgdmFsdWUgY2hhcgBJbnZhbGlkIGhlYWRlciBmaWVsZCBjaGFyAFNwYW4gY2FsbGJhY2sgZXJyb3IgaW4gb25fdmVyc2lvbgBJbnZhbGlkIG1pbm9yIHZlcnNpb24ASW52YWxpZCBtYWpvciB2ZXJzaW9uAEV4cGVjdGVkIHNwYWNlIGFmdGVyIHZlcnNpb24ARXhwZWN0ZWQgQ1JMRiBhZnRlciB2ZXJzaW9uAEludmFsaWQgSFRUUCB2ZXJzaW9uAEludmFsaWQgaGVhZGVyIHRva2VuAFNwYW4gY2FsbGJhY2sgZXJyb3IgaW4gb25fdXJsAEludmFsaWQgY2hhcmFjdGVycyBpbiB1cmwAVW5leHBlY3RlZCBzdGFydCBjaGFyIGluIHVybABEb3VibGUgQCBpbiB1cmwARW1wdHkgQ29udGVudC1MZW5ndGgASW52YWxpZCBjaGFyYWN0ZXIgaW4gQ29udGVudC1MZW5ndGgARHVwbGljYXRlIENvbnRlbnQtTGVuZ3RoAEludmFsaWQgY2hhciBpbiB1cmwgcGF0aABDb250ZW50LUxlbmd0aCBjYW4ndCBiZSBwcmVzZW50IHdpdGggVHJhbnNmZXItRW5jb2RpbmcASW52YWxpZCBjaGFyYWN0ZXIgaW4gY2h1bmsgc2l6ZQBTcGFuIGNhbGxiYWNrIGVycm9yIGluIG9uX2hlYWRlcl92YWx1ZQBTcGFuIGNhbGxiYWNrIGVycm9yIGluIG9uX2NodW5rX2V4dGVuc2lvbl92YWx1ZQBJbnZhbGlkIGNoYXJhY3RlciBpbiBjaHVuayBleHRlbnNpb25zIHZhbHVlAE1pc3NpbmcgZXhwZWN0ZWQgTEYgYWZ0ZXIgaGVhZGVyIHZhbHVlAEludmFsaWQgYFRyYW5zZmVyLUVuY29kaW5nYCBoZWFkZXIgdmFsdWUASW52YWxpZCBjaGFyYWN0ZXIgaW4gY2h1bmsgZXh0ZW5zaW9ucyBxdW90ZSB2YWx1ZQBJbnZhbGlkIGNoYXJhY3RlciBpbiBjaHVuayBleHRlbnNpb25zIHF1b3RlZCB2YWx1ZQBQYXVzZWQgYnkgb25faGVhZGVyc19jb21wbGV0ZQBJbnZhbGlkIEVPRiBzdGF0ZQBvbl9yZXNldCBwYXVzZQBvbl9jaHVua19oZWFkZXIgcGF1c2UAb25fbWVzc2FnZV9iZWdpbiBwYXVzZQBvbl9jaHVua19leHRlbnNpb25fdmFsdWUgcGF1c2UAb25fc3RhdHVzX2NvbXBsZXRlIHBhdXNlAG9uX3ZlcnNpb25fY29tcGxldGUgcGF1c2UAb25fdXJsX2NvbXBsZXRlIHBhdXNlAG9uX2NodW5rX2NvbXBsZXRlIHBhdXNlAG9uX2hlYWRlcl92YWx1ZV9jb21wbGV0ZSBwYXVzZQBvbl9tZXNzYWdlX2NvbXBsZXRlIHBhdXNlAG9uX21ldGhvZF9jb21wbGV0ZSBwYXVzZQBvbl9oZWFkZXJfZmllbGRfY29tcGxldGUgcGF1c2UAb25fY2h1bmtfZXh0ZW5zaW9uX25hbWUgcGF1c2UAVW5leHBlY3RlZCBzcGFjZSBhZnRlciBzdGFydCBsaW5lAFNwYW4gY2FsbGJhY2sgZXJyb3IgaW4gb25fY2h1bmtfZXh0ZW5zaW9uX25hbWUASW52YWxpZCBjaGFyYWN0ZXIgaW4gY2h1bmsgZXh0ZW5zaW9ucyBuYW1lAFBhdXNlIG9uIENPTk5FQ1QvVXBncmFkZQBQYXVzZSBvbiBQUkkvVXBncmFkZQBFeHBlY3RlZCBIVFRQLzIgQ29ubmVjdGlvbiBQcmVmYWNlAFNwYW4gY2FsbGJhY2sgZXJyb3IgaW4gb25fbWV0aG9kAEV4cGVjdGVkIHNwYWNlIGFmdGVyIG1ldGhvZABTcGFuIGNhbGxiYWNrIGVycm9yIGluIG9uX2hlYWRlcl9maWVsZABQYXVzZWQASW52YWxpZCB3b3JkIGVuY291bnRlcmVkAEludmFsaWQgbWV0aG9kIGVuY291bnRlcmVkAFVuZXhwZWN0ZWQgY2hhciBpbiB1cmwgc2NoZW1hAFJlcXVlc3QgaGFzIGludmFsaWQgYFRyYW5zZmVyLUVuY29kaW5nYABTV0lUQ0hfUFJPWFkAVVNFX1BST1hZAE1LQUNUSVZJVFkAVU5QUk9DRVNTQUJMRV9FTlRJVFkAQ09QWQBNT1ZFRF9QRVJNQU5FTlRMWQBUT09fRUFSTFkATk9USUZZAEZBSUxFRF9ERVBFTkRFTkNZAEJBRF9HQVRFV0FZAFBMQVkAUFVUAENIRUNLT1VUAEdBVEVXQVlfVElNRU9VVABSRVFVRVNUX1RJTUVPVVQATkVUV09SS19DT05ORUNUX1RJTUVPVVQAQ09OTkVDVElPTl9USU1FT1VUAExPR0lOX1RJTUVPVVQATkVUV09SS19SRUFEX1RJTUVPVVQAUE9TVABNSVNESVJFQ1RFRF9SRVFVRVNUAENMSUVOVF9DTE9TRURfUkVRVUVTVABDTElFTlRfQ0xPU0VEX0xPQURfQkFMQU5DRURfUkVRVUVTVABCQURfUkVRVUVTVABIVFRQX1JFUVVFU1RfU0VOVF9UT19IVFRQU19QT1JUAFJFUE9SVABJTV9BX1RFQVBPVABSRVNFVF9DT05URU5UAE5PX0NPTlRFTlQAUEFSVElBTF9DT05URU5UAEhQRV9JTlZBTElEX0NPTlNUQU5UAEhQRV9DQl9SRVNFVABHRVQASFBFX1NUUklDVABDT05GTElDVABURU1QT1JBUllfUkVESVJFQ1QAUEVSTUFORU5UX1JFRElSRUNUAENPTk5FQ1QATVVMVElfU1RBVFVTAEhQRV9JTlZBTElEX1NUQVRVUwBUT09fTUFOWV9SRVFVRVNUUwBFQVJMWV9ISU5UUwBVTkFWQUlMQUJMRV9GT1JfTEVHQUxfUkVBU09OUwBPUFRJT05TAFNXSVRDSElOR19QUk9UT0NPTFMAVkFSSUFOVF9BTFNPX05FR09USUFURVMATVVMVElQTEVfQ0hPSUNFUwBJTlRFUk5BTF9TRVJWRVJfRVJST1IAV0VCX1NFUlZFUl9VTktOT1dOX0VSUk9SAFJBSUxHVU5fRVJST1IASURFTlRJVFlfUFJPVklERVJfQVVUSEVOVElDQVRJT05fRVJST1IAU1NMX0NFUlRJRklDQVRFX0VSUk9SAElOVkFMSURfWF9GT1JXQVJERURfRk9SAFNFVF9QQVJBTUVURVIAR0VUX1BBUkFNRVRFUgBIUEVfVVNFUgBTRUVfT1RIRVIASFBFX0NCX0NIVU5LX0hFQURFUgBNS0NBTEVOREFSAFNFVFVQAFdFQl9TRVJWRVJfSVNfRE9XTgBURUFSRE9XTgBIUEVfQ0xPU0VEX0NPTk5FQ1RJT04ASEVVUklTVElDX0VYUElSQVRJT04ARElTQ09OTkVDVEVEX09QRVJBVElPTgBOT05fQVVUSE9SSVRBVElWRV9JTkZPUk1BVElPTgBIUEVfSU5WQUxJRF9WRVJTSU9OAEhQRV9DQl9NRVNTQUdFX0JFR0lOAFNJVEVfSVNfRlJPWkVOAEhQRV9JTlZBTElEX0hFQURFUl9UT0tFTgBJTlZBTElEX1RPS0VOAEZPUkJJRERFTgBFTkhBTkNFX1lPVVJfQ0FMTQBIUEVfSU5WQUxJRF9VUkwAQkxPQ0tFRF9CWV9QQVJFTlRBTF9DT05UUk9MAE1LQ09MAEFDTABIUEVfSU5URVJOQUwAUkVRVUVTVF9IRUFERVJfRklFTERTX1RPT19MQVJHRV9VTk9GRklDSUFMAEhQRV9PSwBVTkxJTksAVU5MT0NLAFBSSQBSRVRSWV9XSVRIAEhQRV9JTlZBTElEX0NPTlRFTlRfTEVOR1RIAEhQRV9VTkVYUEVDVEVEX0NPTlRFTlRfTEVOR1RIAEZMVVNIAFBST1BQQVRDSABNLVNFQVJDSABVUklfVE9PX0xPTkcAUFJPQ0VTU0lORwBNSVNDRUxMQU5FT1VTX1BFUlNJU1RFTlRfV0FSTklORwBNSVNDRUxMQU5FT1VTX1dBUk5JTkcASFBFX0lOVkFMSURfVFJBTlNGRVJfRU5DT0RJTkcARXhwZWN0ZWQgQ1JMRgBIUEVfSU5WQUxJRF9DSFVOS19TSVpFAE1PVkUAQ09OVElOVUUASFBFX0NCX1NUQVRVU19DT01QTEVURQBIUEVfQ0JfSEVBREVSU19DT01QTEVURQBIUEVfQ0JfVkVSU0lPTl9DT01QTEVURQBIUEVfQ0JfVVJMX0NPTVBMRVRFAEhQRV9DQl9DSFVOS19DT01QTEVURQBIUEVfQ0JfSEVBREVSX1ZBTFVFX0NPTVBMRVRFAEhQRV9DQl9DSFVOS19FWFRFTlNJT05fVkFMVUVfQ09NUExFVEUASFBFX0NCX0NIVU5LX0VYVEVOU0lPTl9OQU1FX0NPTVBMRVRFAEhQRV9DQl9NRVNTQUdFX0NPTVBMRVRFAEhQRV9DQl9NRVRIT0RfQ09NUExFVEUASFBFX0NCX0hFQURFUl9GSUVMRF9DT01QTEVURQBERUxFVEUASFBFX0lOVkFMSURfRU9GX1NUQVRFAElOVkFMSURfU1NMX0NFUlRJRklDQVRFAFBBVVNFAE5PX1JFU1BPTlNFAFVOU1VQUE9SVEVEX01FRElBX1RZUEUAR09ORQBOT1RfQUNDRVBUQUJMRQBTRVJWSUNFX1VOQVZBSUxBQkxFAFJBTkdFX05PVF9TQVRJU0ZJQUJMRQBPUklHSU5fSVNfVU5SRUFDSEFCTEUAUkVTUE9OU0VfSVNfU1RBTEUAUFVSR0UATUVSR0UAUkVRVUVTVF9IRUFERVJfRklFTERTX1RPT19MQVJHRQBSRVFVRVNUX0hFQURFUl9UT09fTEFSR0UAUEFZTE9BRF9UT09fTEFSR0UASU5TVUZGSUNJRU5UX1NUT1JBR0UASFBFX1BBVVNFRF9VUEdSQURFAEhQRV9QQVVTRURfSDJfVVBHUkFERQBTT1VSQ0UAQU5OT1VOQ0UAVFJBQ0UASFBFX1VORVhQRUNURURfU1BBQ0UAREVTQ1JJQkUAVU5TVUJTQ1JJQkUAUkVDT1JEAEhQRV9JTlZBTElEX01FVEhPRABOT1RfRk9VTkQAUFJPUEZJTkQAVU5CSU5EAFJFQklORABVTkFVVEhPUklaRUQATUVUSE9EX05PVF9BTExPV0VEAEhUVFBfVkVSU0lPTl9OT1RfU1VQUE9SVEVEAEFMUkVBRFlfUkVQT1JURUQAQUNDRVBURUQATk9UX0lNUExFTUVOVEVEAExPT1BfREVURUNURUQASFBFX0NSX0VYUEVDVEVEAEhQRV9MRl9FWFBFQ1RFRABDUkVBVEVEAElNX1VTRUQASFBFX1BBVVNFRABUSU1FT1VUX09DQ1VSRUQAUEFZTUVOVF9SRVFVSVJFRABQUkVDT05ESVRJT05fUkVRVUlSRUQAUFJPWFlfQVVUSEVOVElDQVRJT05fUkVRVUlSRUQATkVUV09SS19BVVRIRU5USUNBVElPTl9SRVFVSVJFRABMRU5HVEhfUkVRVUlSRUQAU1NMX0NFUlRJRklDQVRFX1JFUVVJUkVEAFVQR1JBREVfUkVRVUlSRUQAUEFHRV9FWFBJUkVEAFBSRUNPTkRJVElPTl9GQUlMRUQARVhQRUNUQVRJT05fRkFJTEVEAFJFVkFMSURBVElPTl9GQUlMRUQAU1NMX0hBTkRTSEFLRV9GQUlMRUQATE9DS0VEAFRSQU5TRk9STUFUSU9OX0FQUExJRUQATk9UX01PRElGSUVEAE5PVF9FWFRFTkRFRABCQU5EV0lEVEhfTElNSVRfRVhDRUVERUQAU0lURV9JU19PVkVSTE9BREVEAEhFQUQARXhwZWN0ZWQgSFRUUC8AAF4TAAAmEwAAMBAAAPAXAACdEwAAFRIAADkXAADwEgAAChAAAHUSAACtEgAAghMAAE8UAAB/EAAAoBUAACMUAACJEgAAixQAAE0VAADUEQAAzxQAABAYAADJFgAA3BYAAMERAADgFwAAuxQAAHQUAAB8FQAA5RQAAAgXAAAfEAAAZRUAAKMUAAAoFQAAAhUAAJkVAAAsEAAAixkAAE8PAADUDgAAahAAAM4QAAACFwAAiQ4AAG4TAAAcEwAAZhQAAFYXAADBEwAAzRMAAGwTAABoFwAAZhcAAF8XAAAiEwAAzg8AAGkOAADYDgAAYxYAAMsTAACqDgAAKBcAACYXAADFEwAAXRYAAOgRAABnEwAAZRMAAPIWAABzEwAAHRcAAPkWAADzEQAAzw4AAM4VAAAMEgAAsxEAAKURAABhEAAAMhcAALsTAEH5NQsBAQBBkDYL4AEBAQIBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQBB/TcLAQEAQZE4C14CAwICAgICAAACAgACAgACAgICAgICAgICAAQAAAAAAAICAgICAgICAgICAgICAgICAgICAgICAgICAAAAAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAAgACAEH9OQsBAQBBkToLXgIAAgICAgIAAAICAAICAAICAgICAgICAgIAAwAEAAAAAgICAgICAgICAgICAgICAgICAgICAgICAgIAAAACAgICAgICAgICAgICAgICAgICAgICAgICAgICAgACAAIAQfA7Cw1sb3NlZWVwLWFsaXZlAEGJPAsBAQBBoDwL4AEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQBBiT4LAQEAQaA+C+cBAQEBAQEBAQEBAQEBAgEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQFjaHVua2VkAEGwwAALXwEBAAEBAQEBAAABAQABAQABAQEBAQEBAQEBAAAAAAAAAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAAAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAQABAEGQwgALIWVjdGlvbmVudC1sZW5ndGhvbnJveHktY29ubmVjdGlvbgBBwMIACy1yYW5zZmVyLWVuY29kaW5ncGdyYWRlDQoNCg0KU00NCg0KVFRQL0NFL1RTUC8AQfnCAAsFAQIAAQMAQZDDAAvgAQQBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAEH5xAALBQECAAEDAEGQxQAL4AEEAQEFAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQBB+cYACwQBAAABAEGRxwAL3wEBAQABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAEH6yAALBAEAAAIAQZDJAAtfAwQAAAQEBAQEBAQEBAQEBQQEBAQEBAQEBAQEBAAEAAYHBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAQABAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAAAAQAQfrKAAsEAQAAAQBBkMsACwEBAEGqywALQQIAAAAAAAADAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwAAAAAAAAMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAEH6zAALBAEAAAEAQZDNAAsBAQBBms0ACwYCAAAAAAIAQbHNAAs6AwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMAAAAAAAADAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwBB8M4AC5YBTk9VTkNFRUNLT1VUTkVDVEVURUNSSUJFTFVTSEVURUFEU0VBUkNIUkdFQ1RJVklUWUxFTkRBUlZFT1RJRllQVElPTlNDSFNFQVlTVEFUQ0hHRU9SRElSRUNUT1JUUkNIUEFSQU1FVEVSVVJDRUJTQ1JJQkVBUkRPV05BQ0VJTkROS0NLVUJTQ1JJQkVIVFRQL0FEVFAv", "base64"); } }); -// .yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/llhttp/llhttp_simd-wasm.js +// .yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/llhttp/llhttp_simd-wasm.js var require_llhttp_simd_wasm = __commonJS({ - ".yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/llhttp/llhttp_simd-wasm.js"(exports2, module2) { + ".yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/llhttp/llhttp_simd-wasm.js"(exports2, module2) { "use strict"; var { Buffer: Buffer3 } = require("node:buffer"); module2.exports = Buffer3.from("AGFzbQEAAAABJwdgAX8Bf2ADf39/AX9gAX8AYAJ/fwBgBH9/f38Bf2AAAGADf39/AALLAQgDZW52GHdhc21fb25faGVhZGVyc19jb21wbGV0ZQAEA2VudhV3YXNtX29uX21lc3NhZ2VfYmVnaW4AAANlbnYLd2FzbV9vbl91cmwAAQNlbnYOd2FzbV9vbl9zdGF0dXMAAQNlbnYUd2FzbV9vbl9oZWFkZXJfZmllbGQAAQNlbnYUd2FzbV9vbl9oZWFkZXJfdmFsdWUAAQNlbnYMd2FzbV9vbl9ib2R5AAEDZW52GHdhc21fb25fbWVzc2FnZV9jb21wbGV0ZQAAAy0sBQYAAAIAAAAAAAACAQIAAgICAAADAAAAAAMDAwMBAQEBAQEBAQEAAAIAAAAEBQFwARISBQMBAAIGCAF/AUGA1AQLB9EFIgZtZW1vcnkCAAtfaW5pdGlhbGl6ZQAIGV9faW5kaXJlY3RfZnVuY3Rpb25fdGFibGUBAAtsbGh0dHBfaW5pdAAJGGxsaHR0cF9zaG91bGRfa2VlcF9hbGl2ZQAvDGxsaHR0cF9hbGxvYwALBm1hbGxvYwAxC2xsaHR0cF9mcmVlAAwEZnJlZQAMD2xsaHR0cF9nZXRfdHlwZQANFWxsaHR0cF9nZXRfaHR0cF9tYWpvcgAOFWxsaHR0cF9nZXRfaHR0cF9taW5vcgAPEWxsaHR0cF9nZXRfbWV0aG9kABAWbGxodHRwX2dldF9zdGF0dXNfY29kZQAREmxsaHR0cF9nZXRfdXBncmFkZQASDGxsaHR0cF9yZXNldAATDmxsaHR0cF9leGVjdXRlABQUbGxodHRwX3NldHRpbmdzX2luaXQAFQ1sbGh0dHBfZmluaXNoABYMbGxodHRwX3BhdXNlABcNbGxodHRwX3Jlc3VtZQAYG2xsaHR0cF9yZXN1bWVfYWZ0ZXJfdXBncmFkZQAZEGxsaHR0cF9nZXRfZXJybm8AGhdsbGh0dHBfZ2V0X2Vycm9yX3JlYXNvbgAbF2xsaHR0cF9zZXRfZXJyb3JfcmVhc29uABwUbGxodHRwX2dldF9lcnJvcl9wb3MAHRFsbGh0dHBfZXJybm9fbmFtZQAeEmxsaHR0cF9tZXRob2RfbmFtZQAfEmxsaHR0cF9zdGF0dXNfbmFtZQAgGmxsaHR0cF9zZXRfbGVu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PgtBhwEMPQtBiAEMPAtBiQEMOwtBigEMOgtBiwEMOQtBjAEMOAtBjQEMNwtBjgEMNgtBjwEMNQtBkAEMNAtBkQEMMwtBkgEMMgtBkwEMMQtBlAEMMAtBlQEMLwtBlgEMLgtBlwEMLQtBmAEMLAtBmQEMKwtBmgEMKgtBmwEMKQtBnAEMKAtBnQEMJwtBngEMJgtBnwEMJQtBoAEMJAtBoQEMIwtBogEMIgtBowEMIQtBpAEMIAtBpQEMHwtBpgEMHgtBpwEMHQtBqAEMHAtBqQEMGwtBqgEMGgtBqwEMGQtBrAEMGAtBrQEMFwtBrgEMFgtBAQwVC0GvAQwUC0GwAQwTC0GxAQwSC0GzAQwRC0GyAQwQC0G0AQwPC0G1AQwOC0G2AQwNC0G3AQwMC0G4AQwLC0G5AQwKC0G6AQwJC0G7AQwIC0HGAQwHC0G8AQwGC0G9AQwFC0G+AQwEC0G/AQwDC0HAAQwCC0HCAQwBC0HBAQshAwNAAkACQAJAAkACQAJAAkACQAJAIAICfwJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJ/AkACQAJAAkACQAJAAkACQAJAAkACQAJAAkAgAgJ/AkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACfwJAAkACfwJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACfwJAAkACQAJAAn8CQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQCADDsYBAAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHyAhIyUmKCorLC8wMTIzNDU2Nzk6Ozw9lANAQkRFRklLTk9QUVJTVFVWWFpbXF1eX2BhYmNkZWZnaGpsb3Bxc3V2eHl6e3x/gAGBAYIBgwGEAYUBhgGHAYgBiQGKAYsBjAGNAY4BjwGQAZEBkgGTAZQBlQGWAZcBmAGZAZoBmwGcAZ0BngGfAaABoQGiAaMBpAGlAaYBpwGoAakBqgGrAawBrQGuAa8BsAGxAbIBswG0AbUBtgG3AbgBuQG6AbsBvAG9Ab4BvwHAAcEBwgHDAcQBxQHGAccByAHJAcsBzAHNAc4BzwGKA4kDiAOHA4QDgwOAA/sC+gL5AvgC9wL0AvMC8gLLAsECsALZAQsgASAERw3wAkHdASEDDLMDCyABIARHDcgBQcMBIQMMsgMLIAEgBEcNe0H3ACEDDLEDCyABIARHDXBB7wAhAwywAwsgASAERw1pQeoAIQMMrwMLIAEgBEcNZUHoACEDDK4DCyABIARHDWJB5gAhAwytAwsgASAERw0aQRghAwysAwsgASAERw0VQRIhAwyrAwsgASAERw1CQcUAIQMMqgMLIAEgBEcNNEE/IQMMqQMLIAEgBEcNMkE8IQMMqAMLIAEgBEcNK0ExIQMMpwMLIAItAC5BAUYNnwMMwQILQQAhAAJAAkACQCACLQAqRQ0AIAItACtFDQAgAi8BMCIDQQJxRQ0BDAILIAIvATAiA0EBcUUNAQtBASEAIAItAChBAUYNACACLwEyIgVB5ABrQeQASQ0AIAVBzAFGDQAgBUGwAkYNACADQcAAcQ0AQQAhACADQYgEcUGABEYNACADQShxQQBHIQALIAJBADsBMCACQQA6AC8gAEUN3wIgAkIANwMgDOACC0EAIQACQCACKAI4IgNFDQAgAygCLCIDRQ0AIAIgAxEAACEACyAARQ3MASAAQRVHDd0CIAJBBDYCHCACIAE2AhQgAkGwGDYCECACQRU2AgxBACEDDKQDCyABIARGBEBBBiEDDKQDCyABQQFqIQFBACEAAkAgAigCOCIDRQ0AIAMoAlQiA0UNACACIAMRAAAhAAsgAA3ZAgwcCyACQgA3AyBBEiEDDIkDCyABIARHDRZBHSEDDKEDCyABIARHBEAgAUEBaiEBQRAhAwyIAwtBByEDDKADCyACIAIpAyAiCiAEIAFrrSILfSIMQgAgCiAMWhs3AyAgCiALWA3UAkEIIQMMnwMLIAEgBEcEQCACQQk2AgggAiABNgIEQRQhAwyGAwtBCSEDDJ4DCyACKQMgQgBSDccBIAIgAi8BMEGAAXI7ATAMQgsgASAERw0/QdAAIQMMnAMLIAEgBEYEQEELIQMMnAMLIAFBAWohAUEAIQACQCACKAI4IgNFDQAgAygCUCIDRQ0AIAIgAxEAACEACyAADc8CDMYBC0EAIQACQCACKAI4IgNFDQAgAygCSCIDRQ0AIAIgAxEAACEACyAARQ3GASAAQRVHDc0CIAJBCzYCHCACIAE2AhQgAkGCGTYCECACQRU2AgxBACEDDJoDC0EAIQACQCACKAI4IgNFDQAgAygCSCIDRQ0AIAIgAxEAACEACyAARQ0MIABBFUcNygIgAkEaNgIcIAIgATYCFCACQYIZNgIQIAJBFTYCDEEAIQMMmQMLQQAhAAJAIAIoAjgiA0UNACADKAJMIgNFDQAgAiADEQAAIQALIABFDcQBIABBFUcNxwIgAkELNgIcIAIgATYCFCACQZEXNgIQIAJBFTYCDEEAIQMMmAMLIAEgBEYEQEEPIQMMmAMLIAEtAAAiAEE7Rg0HIABBDUcNxAIgAUEBaiEBDMMBC0EAIQACQCACKAI4IgNFDQAgAygCTCIDRQ0AIAIgAxEAACEACyAARQ3DASAAQRVHDcICIAJBDzYCHCACIAE2AhQgAkGRFzYCECACQRU2AgxBACEDDJYDCwNAIAEtAABB8DVqLQAAIgBBAUcEQCAAQQJHDcECIAIoAgQhAEEAIQMgAkEANgIEIAIgACABQQFqIgEQLSIADcICDMUBCyAEIAFBAWoiAUcNAAtBEiEDDJUDC0EAIQACQCACKAI4IgNFDQAgAygCTCIDRQ0AIAIgAxEAACEACyAARQ3FASAAQRVHDb0CIAJBGzYCHCACIAE2AhQgAkGRFzYCECACQRU2AgxBACEDDJQDCyABIARGBEBBFiEDDJQDCyACQQo2AgggAiABNgIEQQAhAAJAIAIoAjgiA0UNACADKAJIIgNFDQAgAiADEQAAIQALIABFDcIBIABBFUcNuQIgAkEVNgIcIAIgATYCFCACQYIZNgIQIAJBFTYCDEEAIQMMkwMLIAEgBEcEQANAIAEtAABB8DdqLQAAIgBBAkcEQAJAIABBAWsOBMQCvQIAvgK9AgsgAUEBaiEBQQghAwz8AgsgBCABQQFqIgFHDQALQRUhAwyTAwtBFSEDDJIDCwNAIAEtAABB8DlqLQAAIgBBAkcEQCAAQQFrDgTFArcCwwK4ArcCCyAEIAFBAWoiAUcNAAtBGCEDDJEDCyABIARHBEAgAkELNgIIIAIgATYCBEEHIQMM+AILQRkhAwyQAwsgAUEBaiEBDAILIAEgBEYEQEEaIQMMjwMLAkAgAS0AAEENaw4UtQG/Ab8BvwG/Ab8BvwG/Ab8BvwG/Ab8BvwG/Ab8BvwG/Ab8BvwEAvwELQQAhAyACQQA2AhwgAkGvCzYCECACQQI2AgwgAiABQQFqNgIUDI4DCyABIARGBEBBGyEDDI4DCyABLQAAIgBBO0cEQCAAQQ1HDbECIAFBAWohAQy6AQsgAUEBaiEBC0EiIQMM8wILIAEgBEYEQEEcIQMMjAMLQgAhCgJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkAgAS0AAEEwaw43wQLAAgABAgMEBQYH0AHQAdAB0AHQAdAB0AEICQoLDA3QAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdAB0AHQAdABDg8QERIT0AELQgIhCgzAAgtCAyEKDL8CC0IEIQoMvgILQgUhCgy9AgtCBiEKDLwCC0IHIQoMuwILQgghCgy6AgtCCSEKDLkCC0IKIQoMuAILQgshCgy3AgtCDCEKDLYCC0INIQoMtQILQg4hCgy0AgtCDyEKDLMCC0IKIQoMsgILQgshCgyxAgtCDCEKDLACC0INIQoMrwILQg4hCgyuAgtCDyEKDK0CC0IAIQoCQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAIAEtAABBMGsON8ACvwIAAQIDBAUGB74CvgK+Ar4CvgK+Ar4CCAkKCwwNvgK+Ar4CvgK+Ar4CvgK+Ar4CvgK+Ar4CvgK+Ar4CvgK+Ar4CvgK+Ar4CvgK+Ar4CvgK+Ag4PEBESE74CC0ICIQoMvwILQgMhCgy+AgtCBCEKDL0CC0IFIQoMvAILQgYhCgy7AgtCByEKDLoCC0IIIQoMuQILQgkhCgy4AgtCCiEKDLcCC0ILIQoMtgILQgwhCgy1AgtCDSEKDLQCC0IOIQoMswILQg8hCgyyAgtCCiEKDLECC0ILIQoMsAILQgwhCgyvAgtCDSEKDK4CC0IOIQoMrQILQg8hCgysAgsgAiACKQMgIgogBCABa60iC30iDEIAIAogDFobNwMgIAogC1gNpwJBHyEDDIkDCyABIARHBEAgAkEJNgIIIAIgATYCBEElIQMM8AILQSAhAwyIAwtBASEFIAIvATAiA0EIcUUEQCACKQMgQgBSIQULAkAgAi0ALgRAQQEhACACLQApQQVGDQEgA0HAAHFFIAVxRQ0BC0EAIQAgA0HAAHENAEECIQAgA0EIcQ0AIANBgARxBEACQCACLQAoQQFHDQAgAi0ALUEKcQ0AQQUhAAwCC0EEIQAMAQsgA0EgcUUEQAJAIAItAChBAUYNACACLwEyIgBB5ABrQeQASQ0AIABBzAFGDQAgAEGwAkYNAEEEIQAgA0EocUUNAiADQYgEcUGABEYNAgtBACEADAELQQBBAyACKQMgUBshAAsgAEEBaw4FvgIAsAEBpAKhAgtBESEDDO0CCyACQQE6AC8MhAMLIAEgBEcNnQJBJCEDDIQDCyABIARHDRxBxgAhAwyDAwtBACEAAkAgAigCOCIDRQ0AIAMoAkQiA0UNACACIAMRAAAhAAsgAEUNJyAAQRVHDZgCIAJB0AA2AhwgAiABNgIUIAJBkRg2AhAgAkEVNgIMQQAhAwyCAwsgASAERgRAQSghAwyCAwtBACEDIAJBADYCBCACQQw2AgggAiABIAEQKiIARQ2UAiACQSc2AhwgAiABNgIUIAIgADYCDAyBAwsgASAERgRAQSkhAwyBAwsgAS0AACIAQSBGDRMgAEEJRw2VAiABQQFqIQEMFAsgASAERwRAIAFBAWohAQwWC0EqIQMM/wILIAEgBEYEQEErIQMM/wILIAEtAAAiAEEJRyAAQSBHcQ2QAiACLQAsQQhHDd0CIAJBADoALAzdAgsgASAERgRAQSwhAwz+AgsgAS0AAEEKRw2OAiABQQFqIQEMsAELIAEgBEcNigJBLyEDDPwCCwNAIAEtAAAiAEEgRwRAIABBCmsOBIQCiAKIAoQChgILIAQgAUEBaiIBRw0AC0ExIQMM+wILQTIhAyABIARGDfoCIAIoAgAiACAEIAFraiEHIAEgAGtBA2ohBgJAA0AgAEHwO2otAAAgAS0AACIFQSByIAUgBUHBAGtB/wFxQRpJG0H/AXFHDQEgAEEDRgRAQQYhAQziAgsgAEEBaiEAIAQgAUEBaiIBRw0ACyACIAc2AgAM+wILIAJBADYCAAyGAgtBMyEDIAQgASIARg35AiAEIAFrIAIoAgAiAWohByAAIAFrQQhqIQYCQANAIAFB9DtqLQAAIAAtAAAiBUEgciAFIAVBwQBrQf8BcUEaSRtB/wFxRw0BIAFBCEYEQEEFIQEM4QILIAFBAWohASAEIABBAWoiAEcNAAsgAiAHNgIADPoCCyACQQA2AgAgACEBDIUCC0E0IQMgBCABIgBGDfgCIAQgAWsgAigCACIBaiEHIAAgAWtBBWohBgJAA0AgAUHQwgBqLQAAIAAtAAAiBUEgciAFIAVBwQBrQf8BcUEaSRtB/wFxRw0BIAFBBUYEQEEHIQEM4AILIAFBAWohASAEIABBAWoiAEcNAAsgAiAHNgIADPkCCyACQQA2AgAgACEBDIQCCyABIARHBEADQCABLQAAQYA+ai0AACIAQQFHBEAgAEECRg0JDIECCyAEIAFBAWoiAUcNAAtBMCEDDPgCC0EwIQMM9wILIAEgBEcEQANAIAEtAAAiAEEgRwRAIABBCmsOBP8B/gH+Af8B/gELIAQgAUEBaiIBRw0AC0E4IQMM9wILQTghAwz2AgsDQCABLQAAIgBBIEcgAEEJR3EN9gEgBCABQQFqIgFHDQALQTwhAwz1AgsDQCABLQAAIgBBIEcEQAJAIABBCmsOBPkBBAT5AQALIABBLEYN9QEMAwsgBCABQQFqIgFHDQALQT8hAwz0AgtBwAAhAyABIARGDfMCIAIoAgAiACAEIAFraiEFIAEgAGtBBmohBgJAA0AgAEGAQGstAAAgAS0AAEEgckcNASAAQQZGDdsCIABBAWohACAEIAFBAWoiAUcNAAsgAiAFNgIADPQCCyACQQA2AgALQTYhAwzZAgsgASAERgRAQcEAIQMM8gILIAJBDDYCCCACIAE2AgQgAi0ALEEBaw4E+wHuAewB6wHUAgsgAUEBaiEBDPoBCyABIARHBEADQAJAIAEtAAAiAEEgciAAIABBwQBrQf8BcUEaSRtB/wFxIgBBCUYNACAAQSBGDQACQAJAAkACQCAAQeMAaw4TAAMDAwMDAwMBAwMDAwMDAwMDAgMLIAFBAWohAUExIQMM3AILIAFBAWohAUEyIQMM2wILIAFBAWohAUEzIQMM2gILDP4BCyAEIAFBAWoiAUcNAAtBNSEDDPACC0E1IQMM7wILIAEgBEcEQANAIAEtAABBgDxqLQAAQQFHDfcBIAQgAUEBaiIBRw0AC0E9IQMM7wILQT0hAwzuAgtBACEAAkAgAigCOCIDRQ0AIAMoAkAiA0UNACACIAMRAAAhAAsgAEUNASAAQRVHDeYBIAJBwgA2AhwgAiABNgIUIAJB4xg2AhAgAkEVNgIMQQAhAwztAgsgAUEBaiEBC0E8IQMM0gILIAEgBEYEQEHCACEDDOsCCwJAA0ACQCABLQAAQQlrDhgAAswCzALRAswCzALMAswCzALMAswCzALMAswCzALMAswCzALMAswCzALMAgDMAgsgBCABQQFqIgFHDQALQcIAIQMM6wILIAFBAWohASACLQAtQQFxRQ3+AQtBLCEDDNACCyABIARHDd4BQcQAIQMM6AILA0AgAS0AAEGQwABqLQAAQQFHDZwBIAQgAUEBaiIBRw0AC0HFACEDDOcCCyABLQAAIgBBIEYN/gEgAEE6Rw3AAiACKAIEIQBBACEDIAJBADYCBCACIAAgARApIgAN3gEM3QELQccAIQMgBCABIgBGDeUCIAQgAWsgAigCACIBaiEHIAAgAWtBBWohBgNAIAFBkMIAai0AACAALQAAIgVBIHIgBSAFQcEAa0H/AXFBGkkbQf8BcUcNvwIgAUEFRg3CAiABQQFqIQEgBCAAQQFqIgBHDQALIAIgBzYCAAzlAgtByAAhAyAEIAEiAEYN5AIgBCABayACKAIAIgFqIQcgACABa0EJaiEGA0AgAUGWwgBqLQAAIAAtAAAiBUEgciAFIAVBwQBrQf8BcUEaSRtB/wFxRw2+AkECIAFBCUYNwgIaIAFBAWohASAEIABBAWoiAEcNAAsgAiAHNgIADOQCCyABIARGBEBByQAhAwzkAgsCQAJAIAEtAAAiAEEgciAAIABBwQBrQf8BcUEaSRtB/wFxQe4Aaw4HAL8CvwK/Ar8CvwIBvwILIAFBAWohAUE+IQMMywILIAFBAWohAUE/IQMMygILQcoAIQMgBCABIgBGDeICIAQgAWsgAigCACIBaiEGIAAgAWtBAWohBwNAIAFBoMIAai0AACAALQAAIgVBIHIgBSAFQcEAa0H/AXFBGkkbQf8BcUcNvAIgAUEBRg2+AiABQQFqIQEgBCAAQQFqIgBHDQALIAIgBjYCAAziAgtBywAhAyAEIAEiAEYN4QIgBCABayACKAIAIgFqIQcgACABa0EOaiEGA0AgAUGiwgBqLQAAIAAtAAAiBUEgciAFIAVBwQBrQf8BcUEaSRtB/wFxRw27AiABQQ5GDb4CIAFBAWohASAEIABBAWoiAEcNAAsgAiAHNgIADOECC0HMACEDIAQgASIARg3gAiAEIAFrIAIoAgAiAWohByAAIAFrQQ9qIQYDQCABQcDCAGotAAAgAC0AACIFQSByIAUgBUHBAGtB/wFxQRpJG0H/AXFHDboCQQMgAUEPRg2+AhogAUEBaiEBIAQgAEEBaiIARw0ACyACIAc2AgAM4AILQc0AIQMgBCABIgBGDd8CIAQgAWsgAigCACIBaiEHIAAgAWtBBWohBgNAIAFB0MIAai0AACAALQAAIgVBIHIgBSAFQcEAa0H/AXFBGkkbQf8BcUcNuQJBBCABQQVGDb0CGiABQQFqIQEgBCAAQQFqIgBHDQALIAIgBzYCAAzfAgsgASAERgRAQc4AIQMM3wILAkACQAJAAkAgAS0AACIAQSByIAAgAEHBAGtB/wFxQRpJG0H/AXFB4wBrDhMAvAK8ArwCvAK8ArwCvAK8ArwCvAK8ArwCAbwCvAK8AgIDvAILIAFBAWohAUHBACEDDMgCCyABQQFqIQFBwgAhAwzHAgsgAUEBaiEBQcMAIQMMxgILIAFBAWohAUHEACEDDMUCCyABIARHBEAgAkENNgIIIAIgATYCBEHFACEDDMUCC0HPACEDDN0CCwJAAkAgAS0AAEEKaw4EAZABkAEAkAELIAFBAWohAQtBKCEDDMMCCyABIARGBEBB0QAhAwzcAgsgAS0AAEEgRw0AIAFBAWohASACLQAtQQFxRQ3QAQtBFyEDDMECCyABIARHDcsBQdIAIQMM2QILQdMAIQMgASAERg3YAiACKAIAIgAgBCABa2ohBiABIABrQQFqIQUDQCABLQAAIABB1sIAai0AAEcNxwEgAEEBRg3KASAAQQFqIQAgBCABQQFqIgFHDQALIAIgBjYCAAzYAgsgASAERgRAQdUAIQMM2AILIAEtAABBCkcNwgEgAUEBaiEBDMoBCyABIARGBEBB1gAhAwzXAgsCQAJAIAEtAABBCmsOBADDAcMBAcMBCyABQQFqIQEMygELIAFBAWohAUHKACEDDL0CC0EAIQACQCACKAI4IgNFDQAgAygCPCIDRQ0AIAIgAxEAACEACyAADb8BQc0AIQMMvAILIAItAClBIkYNzwIMiQELIAQgASIFRgRAQdsAIQMM1AILQQAhAEEBIQFBASEGQQAhAwJAAn8CQAJAAkACQAJAAkACQCAFLQAAQTBrDgrFAcQBAAECAwQFBgjDAQtBAgwGC0EDDAULQQQMBAtBBQwDC0EGDAILQQcMAQtBCAshA0EAIQFBACEGDL0BC0EJIQNBASEAQQAhAUEAIQYMvAELIAEgBEYEQEHdACEDDNMCCyABLQAAQS5HDbgBIAFBAWohAQyIAQsgASAERw22AUHfACEDDNECCyABIARHBEAgAkEONgIIIAIgATYCBEHQACEDDLgCC0HgACEDDNACC0HhACEDIAEgBEYNzwIgAigCACIAIAQgAWtqIQUgASAAa0EDaiEGA0AgAS0AACAAQeLCAGotAABHDbEBIABBA0YNswEgAEEBaiEAIAQgAUEBaiIBRw0ACyACIAU2AgAMzwILQeIAIQMgASAERg3OAiACKAIAIgAgBCABa2ohBSABIABrQQJqIQYDQCABLQAAIABB5sIAai0AAEcNsAEgAEECRg2vASAAQQFqIQAgBCABQQFqIgFHDQALIAIgBTYCAAzOAgtB4wAhAyABIARGDc0CIAIoAgAiACAEIAFraiEFIAEgAGtBA2ohBgNAIAEtAAAgAEHpwgBqLQAARw2vASAAQQNGDa0BIABBAWohACAEIAFBAWoiAUcNAAsgAiAFNgIADM0CCyABIARGBEBB5QAhAwzNAgsgAUEBaiEBQQAhAAJAIAIoAjgiA0UNACADKAIwIgNFDQAgAiADEQAAIQALIAANqgFB1gAhAwyzAgsgASAERwRAA0AgAS0AACIAQSBHBEACQAJAAkAgAEHIAGsOCwABswGzAbMBswGzAbMBswGzAQKzAQsgAUEBaiEBQdIAIQMMtwILIAFBAWohAUHTACEDDLYCCyABQQFqIQFB1AAhAwy1AgsgBCABQQFqIgFHDQALQeQAIQMMzAILQeQAIQMMywILA0AgAS0AAEHwwgBqLQAAIgBBAUcEQCAAQQJrDgOnAaYBpQGkAQsgBCABQQFqIgFHDQALQeYAIQMMygILIAFBAWogASAERw0CGkHnACEDDMkCCwNAIAEtAABB8MQAai0AACIAQQFHBEACQCAAQQJrDgSiAaEBoAEAnwELQdcAIQMMsQILIAQgAUEBaiIBRw0AC0HoACEDDMgCCyABIARGBEBB6QAhAwzIAgsCQCABLQAAIgBBCmsOGrcBmwGbAbQBmwGbAZsBmwGbAZsBmwGbAZsBmwGbAZsBmwGbAZsBmwGbAZsBpAGbAZsBAJkBCyABQQFqCyEBQQYhAwytAgsDQCABLQAAQfDGAGotAABBAUcNfSAEIAFBAWoiAUcNAAtB6gAhAwzFAgsgAUEBaiABIARHDQIaQesAIQMMxAILIAEgBEYEQEHsACEDDMQCCyABQQFqDAELIAEgBEYEQEHtACEDDMMCCyABQQFqCyEBQQQhAwyoAgsgASAERgRAQe4AIQMMwQILAkACQAJAIAEtAABB8MgAai0AAEEBaw4HkAGPAY4BAHwBAo0BCyABQQFqIQEMCwsgAUEBagyTAQtBACEDIAJBADYCHCACQZsSNgIQIAJBBzYCDCACIAFBAWo2AhQMwAILAkADQCABLQAAQfDIAGotAAAiAEEERwRAAkACQCAAQQFrDgeUAZMBkgGNAQAEAY0BC0HaACEDDKoCCyABQQFqIQFB3AAhAwypAgsgBCABQQFqIgFHDQALQe8AIQMMwAILIAFBAWoMkQELIAQgASIARgRAQfAAIQMMvwILIAAtAABBL0cNASAAQQFqIQEMBwsgBCABIgBGBEBB8QAhAwy+AgsgAC0AACIBQS9GBEAgAEEBaiEBQd0AIQMMpQILIAFBCmsiA0EWSw0AIAAhAUEBIAN0QYmAgAJxDfkBC0EAIQMgAkEANgIcIAIgADYCFCACQYwcNgIQIAJBBzYCDAy8AgsgASAERwRAIAFBAWohAUHeACEDDKMCC0HyACEDDLsCCyABIARGBEBB9AAhAwy7AgsCQCABLQAAQfDMAGotAABBAWsOA/cBcwCCAQtB4QAhAwyhAgsgASAERwRAA0AgAS0AAEHwygBqLQAAIgBBA0cEQAJAIABBAWsOAvkBAIUBC0HfACEDDKMCCyAEIAFBAWoiAUcNAAtB8wAhAwy6AgtB8wAhAwy5AgsgASAERwRAIAJBDzYCCCACIAE2AgRB4AAhAwygAgtB9QAhAwy4AgsgASAERgRAQfYAIQMMuAILIAJBDzYCCCACIAE2AgQLQQMhAwydAgsDQCABLQAAQSBHDY4CIAQgAUEBaiIBRw0AC0H3ACEDDLUCCyABIARGBEBB+AAhAwy1AgsgAS0AAEEgRw16IAFBAWohAQxbC0EAIQACQCACKAI4IgNFDQAgAygCOCIDRQ0AIAIgAxEAACEACyAADXgMgAILIAEgBEYEQEH6ACEDDLMCCyABLQAAQcwARw10IAFBAWohAUETDHYLQfsAIQMgASAERg2xAiACKAIAIgAgBCABa2ohBSABIABrQQVqIQYDQCABLQAAIABB8M4Aai0AAEcNcyAAQQVGDXUgAEEBaiEAIAQgAUEBaiIBRw0ACyACIAU2AgAMsQILIAEgBEYEQEH8ACEDDLECCwJAAkAgAS0AAEHDAGsODAB0dHR0dHR0dHR0AXQLIAFBAWohAUHmACEDDJgCCyABQQFqIQFB5wAhAwyXAgtB/QAhAyABIARGDa8CIAIoAgAiACAEIAFraiEFIAEgAGtBAmohBgJAA0AgAS0AACAAQe3PAGotAABHDXIgAEECRg0BIABBAWohACAEIAFBAWoiAUcNAAsgAiAFNgIADLACCyACQQA2AgAgBkEBaiEBQRAMcwtB/gAhAyABIARGDa4CIAIoAgAiACAEIAFraiEFIAEgAGtBBWohBgJAA0AgAS0AACAAQfbOAGotAABHDXEgAEEFRg0BIABBAWohACAEIAFBAWoiAUcNAAsgAiAFNgIADK8CCyACQQA2AgAgBkEBaiEBQRYMcgtB/wAhAyABIARGDa0CIAIoAgAiACAEIAFraiEFIAEgAGtBA2ohBgJAA0AgAS0AACAAQfzOAGotAABHDXAgAEEDRg0BIABBAWohACAEIAFBAWoiAUcNAAsgAiAFNgIADK4CCyACQQA2AgAgBkEBaiEBQQUMcQsgASAERgRAQYABIQMMrQILIAEtAABB2QBHDW4gAUEBaiEBQQgMcAsgASAERgRAQYEBIQMMrAILAkACQCABLQAAQc4Aaw4DAG8BbwsgAUEBaiEBQesAIQMMkwILIAFBAWohAUHsACEDDJICCyABIARGBEBBggEhAwyrAgsCQAJAIAEtAABByABrDggAbm5ubm5uAW4LIAFBAWohAUHqACEDDJICCyABQQFqIQFB7QAhAwyRAgtBgwEhAyABIARGDakCIAIoAgAiACAEIAFraiEFIAEgAGtBAmohBgJAA0AgAS0AACAAQYDPAGotAABHDWwgAEECRg0BIABBAWohACAEIAFBAWoiAUcNAAsgAiAFNgIADKoCCyACQQA2AgAgBkEBaiEBQQAMbQtBhAEhAyABIARGDagCIAIoAgAiACAEIAFraiEFIAEgAGtBBGohBgJAA0AgAS0AACAAQYPPAGotAABHDWsgAEEERg0BIABBAWohACAEIAFBAWoiAUcNAAsgAiAFNgIADKkCCyACQQA2AgAgBkEBaiEBQSMMbAsgASAERgRAQYUBIQMMqAILAkACQCABLQAAQcwAaw4IAGtra2trawFrCyABQQFqIQFB7wAhAwyPAgsgAUEBaiEBQfAAIQMMjgILIAEgBEYEQEGGASEDDKcCCyABLQAAQcUARw1oIAFBAWohAQxgC0GHASEDIAEgBEYNpQIgAigCACIAIAQgAWtqIQUgASAAa0EDaiEGAkADQCABLQAAIABBiM8Aai0AAEcNaCAAQQNGDQEgAEEBaiEAIAQgAUEBaiIBRw0ACyACIAU2AgAMpgILIAJBADYCACAGQQFqIQFBLQxpC0GIASEDIAEgBEYNpAIgAigCACIAIAQgAWtqIQUgASAAa0EIaiEGAkADQCABLQAAIABB0M8Aai0AAEcNZyAAQQhGDQEgAEEBaiEAIAQgAUEBaiIBRw0ACyACIAU2AgAMpQILIAJBADYCACAGQQFqIQFBKQxoCyABIARGBEBBiQEhAwykAgtBASABLQAAQd8ARw1nGiABQQFqIQEMXgtBigEhAyABIARGDaICIAIoAgAiACAEIAFraiEFIAEgAGtBAWohBgNAIAEtAAAgAEGMzwBqLQAARw1kIABBAUYN+gEgAEEBaiEAIAQgAUEBaiIBRw0ACyACIAU2AgAMogILQYsBIQMgASAERg2hAiACKAIAIgAgBCABa2ohBSABIABrQQJqIQYCQANAIAEtAAAgAEGOzwBqLQAARw1kIABBAkYNASAAQQFqIQAgBCABQQFqIgFHDQALIAIgBTYCAAyiAgsgAkEANgIAIAZBAWohAUECDGULQYwBIQMgASAERg2gAiACKAIAIgAgBCABa2ohBSABIABrQQFqIQYCQANAIAEtAAAgAEHwzwBqLQAARw1jIABBAUYNASAAQQFqIQAgBCABQQFqIgFHDQALIAIgBTYCAAyhAgsgAkEANgIAIAZBAWohAUEfDGQLQY0BIQMgASAERg2fAiACKAIAIgAgBCABa2ohBSABIABrQQFqIQYCQANAIAEtAAAgAEHyzwBqLQAARw1iIABBAUYNASAAQQFqIQAgBCABQQFqIgFHDQALIAIgBTYCAAygAgsgAkEANgIAIAZBAWohAUEJDGMLIAEgBEYEQEGOASEDDJ8CCwJAAkAgAS0AAEHJAGsOBwBiYmJiYgFiCyABQQFqIQFB+AAhAwyGAgsgAUEBaiEBQfkAIQMMhQILQY8BIQMgASAERg2dAiACKAIAIgAgBCABa2ohBSABIABrQQVqIQYCQANAIAEtAAAgAEGRzwBqLQAARw1gIABBBUYNASAAQQFqIQAgBCABQQFqIgFHDQALIAIgBTYCAAyeAgsgAkEANgIAIAZBAWohAUEYDGELQZABIQMgASAERg2cAiACKAIAIgAgBCABa2ohBSABIABrQQJqIQYCQANAIAEtAAAgAEGXzwBqLQAARw1fIABBAkYNASAAQQFqIQAgBCABQQFqIgFHDQALIAIgBTYCAAydAgsgAkEANgIAIAZBAWohAUEXDGALQZEBIQMgASAERg2bAiACKAIAIgAgBCABa2ohBSABIABrQQZqIQYCQANAIAEtAAAgAEGazwBqLQAARw1eIABBBkYNASAAQQFqIQAgBCABQQFqIgFHDQALIAIgBTYCAAycAgsgAkEANgIAIAZBAWohAUEV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JSIARQ0RIAJB5QA2AhwgAiABNgIUIAIgADYCDEEAIQMMtAELIAIoAgQhACACQQA2AgQgAiAAIAEQJSIARQ0gIAJB0wA2AhwgAiABNgIUIAIgADYCDEEAIQMMswELIAIoAgQhACACQQA2AgQgAiAAIAEQJSIARQ0iIAJB0gA2AhwgAiABNgIUIAIgADYCDEEAIQMMsgELIAIoAgQhACACQQA2AgQgAiAAIAEQJSIARQ0OIAJB5QA2AhwgAiABNgIUIAIgADYCDEEAIQMMsQELIAIoAgQhACACQQA2AgQgAiAAIAEQJSIARQ0dIAJB0wA2AhwgAiABNgIUIAIgADYCDEEAIQMMsAELIAIoAgQhACACQQA2AgQgAiAAIAEQJSIARQ0fIAJB0gA2AhwgAiABNgIUIAIgADYCDEEAIQMMrwELIABBP0cNASABQQFqCyEBQQUhAwyUAQtBACEDIAJBADYCHCACIAE2AhQgAkH9EjYCECACQQc2AgwMrAELIAJBADYCHCACIAE2AhQgAkHcCDYCECACQQc2AgxBACEDDKsBCyACKAIEIQAgAkEANgIEIAIgACABECUiAEUNByACQeUANgIcIAIgATYCFCACIAA2AgxBACEDDKoBCyACKAIEIQAgAkEANgIEIAIgACABECUiAEUNFiACQdMANgIcIAIgATYCFCACIAA2AgxBACEDDKkBCyACKAIEIQAgAkEANgIEIAIgACABECUiAEUNGCACQdIANgIcIAIgATYCFCACIAA2AgxBACEDDKgBCyACQQA2AhwgAiABNgIUIAJBxgo2AhAgAkEHNgIMQQAhAwynAQsgAigCBCEAIAJBADYCBCACIAAgARAlIgBFDQMgAkHlADYCHCACIAE2AhQgAiAANgIMQQAhAwymAQsgAigCBCEAIAJBADYCBCACIAAgARAlIgBFDRIgAkHTADYCHCACIAE2AhQgAiAANgIMQQAhAwylAQsgAigCBCEAIAJBADYCBCACIAAgARAlIgBFDRQgAkHSADYCHCACIAE2AhQgAiAANgIMQQAhAwykAQsgAigCBCEAIAJBADYCBCACIAAgARAlIgBFDQAgAkHlADYCHCACIAE2AhQgAiAANgIMQQAhAwyjAQtB1QAhAwyJAQsgAEEVRwRAIAJBADYCHCACIAE2AhQgAkG5DTYCECACQRo2AgxB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AkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAIABB5ABrDvQDY2IAAWFhYWFhYQIDBAVhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhBgcICQoLDA0OD2FhYWFhEGFhYWFhYWFhYWFhEWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYRITFBUWFxgZGhthYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhHB0eHyAhIiMkJSYnKCkqKywtLi8wMTIzNDU2YTc4OTphYWFhYWFhYTthYWE8YWFhYT0+P2FhYWFhYWFhQGFhQWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYUJDREVGR0hJSktMTU5PUFFSU2FhYWFhYWFhVFVWV1hZWlthXF1hYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFeYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhX2BhC0HhJw8LQaQhDwtByywPC0H+MQ8LQcAkDwtBqyQPC0GNKA8LQeImDwtBgDAPC0G5Lw8LQdckDwtB7x8PC0HhHw8LQfofDwtB8iAPC0GoLw8LQa4yDwtBiDAPC0HsJw8LQYIiDwtBjh0PC0HQLg8LQcojDwtBxTIPC0HfHA8LQdIcDwtBxCAPC0HXIA8LQaIfDwtB7S4PC0GrMA8LQdQlDwtBzC4PC0H6Lg8LQfwrDwtB0jAPC0HxHQ8LQbsgDwtB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AAADAEGUCAsFBAAAAAUAQaQICwkGAAAABwAAAAgAQdwIC4otSW52YWxpZCBjaGFyIGluIHVybCBxdWVyeQBTcGFuIGNhbGxiYWNrIGVycm9yIGluIG9uX2JvZHkAQ29udGVudC1MZW5ndGggb3ZlcmZsb3cAQ2h1bmsgc2l6ZSBvdmVyZmxvdwBSZXNwb25zZSBvdmVyZmxvdwBJbnZhbGlkIG1ldGhvZCBmb3IgSFRUUC94LnggcmVxdWVzdABJbnZhbGlkIG1ldGhvZCBmb3IgUlRTUC94LnggcmVxdWVzdABFeHBlY3RlZCBTT1VSQ0UgbWV0aG9kIGZvciBJQ0UveC54IHJlcXVlc3QASW52YWxpZCBjaGFyIGluIHVybCBmcmFnbWVudCBzdGFydABFeHBlY3RlZCBkb3QAU3BhbiBjYWxsYmFjayBlcnJvciBpbiBvbl9zdGF0dXMASW52YWxpZCByZXNwb25zZSBzdGF0dXMASW52YWxpZCBjaGFyYWN0ZXIgaW4gY2h1bmsgZXh0ZW5zaW9ucwBVc2VyIGNhbGxiYWNrIGVycm9yAGBvbl9yZXNldGAgY2FsbGJhY2sgZXJyb3IAYG9uX2NodW5rX2hlYWRlcmAgY2FsbGJhY2sgZXJyb3IAYG9uX21lc3NhZ2VfYmVnaW5gIGNhbGxiYWNrIGVycm9yAGBvbl9jaHVua19leHRlbnNpb25fdmFsdWVgIGNhbGxiYWNrIGVycm9yAGBvbl9zdGF0dXNfY29tcGxldGVgIGNhbGxiYWNrIGVycm9yAGBvbl92ZXJzaW9uX2NvbXBsZXRlYCBjYWxsYmFjayBlcnJvcgBgb25fdXJsX2NvbXBsZXRlYCBjYWxsYmFjayBlcnJvcgBgb25fY2h1bmtfY29tcGxldGVgIGNhbGxiYWNrIGVycm9yAGBvbl9oZWFkZXJfdmFsdWVfY29tcGxldGVgIGNhbGxiYWNrIGVycm9yAGBvbl9tZXNzYWdlX2NvbXBsZXRlYCBjYWxsYmFjayBlcnJvcgBgb25fbWV0aG9kX2NvbXBsZXRlYCBjYWxsYmFjayBlcnJvcgBgb25faGVhZGVyX2ZpZWxkX2NvbXBsZXRlYCBjYWxsYmFjayBlcnJvcgBgb25fY2h1bmtfZXh0ZW5zaW9uX25hbWVgIGNhbGxiYWNrIGVycm9yAFVuZXhwZWN0ZWQgY2hhciBpbiB1cmwgc2VydmVyAEludmFsaWQgaGVhZGVyIHZhbHVlIGNoYXIASW52YWxpZCBoZWFkZXIgZmllbGQgY2hhcgBTcGFuIGNhbGxiYWNrIGVycm9yIGluIG9uX3ZlcnNpb24ASW52YWxpZCBtaW5vciB2ZXJzaW9uAEludmFsaWQgbWFqb3IgdmVyc2lvbgBFeHBlY3RlZCBzcGFjZSBhZnRlciB2ZXJzaW9uAEV4cGVjdGVkIENSTEYgYWZ0ZXIgdmVyc2lvbgBJbnZhbGlkIEhUVFAgdmVyc2lvbgBJbnZhbGlkIGhlYWRlciB0b2tlbgBTcGFuIGNhbGxiYWNrIGVycm9yIGluIG9uX3VybABJbnZhbGlkIGNoYXJhY3RlcnMgaW4gdXJsAFVuZXhwZWN0ZWQgc3RhcnQgY2hhciBpbiB1cmwARG91YmxlIEAgaW4gdXJsAEVtcHR5IENvbnRlbnQtTGVuZ3RoAEludmFsaWQgY2hhcmFjdGVyIGluIENvbnRlbnQtTGVuZ3RoAER1cGxpY2F0ZSBDb250ZW50LUxlbmd0aABJbnZhbGlkIGNoYXIgaW4gdXJsIHBhdGgAQ29udGVudC1MZW5ndGggY2FuJ3QgYmUgcHJlc2VudCB3aXRoIFRyYW5zZmVyLUVuY29kaW5nAEludmFsaWQgY2hhcmFjdGVyIGluIGNodW5rIHNpemUAU3BhbiBjYWxsYmFjayBlcnJvciBpbiBvbl9oZWFkZXJfdmFsdWUAU3BhbiBjYWxsYmFjayBlcnJvciBpbiBvbl9jaHVua19leHRlbnNpb25fdmFsdWUASW52YWxpZCBjaGFyYWN0ZXIgaW4gY2h1bmsgZXh0ZW5zaW9ucyB2YWx1ZQBNaXNzaW5nIGV4cGVjdGVkIExGIGFmdGVyIGhlYWRlciB2YWx1ZQBJbnZhbGlkIGBUcmFuc2Zlci1FbmNvZGluZ2AgaGVhZGVyIHZhbHVlAEludmFsaWQgY2hhcmFjdGVyIGluIGNodW5rIGV4dGVuc2lvbnMgcXVvdGUgdmFsdWUASW52YWxpZCBjaGFyYWN0ZXIgaW4gY2h1bmsgZXh0ZW5zaW9ucyBxdW90ZWQgdmFsdWUAUGF1c2VkIGJ5IG9uX2hlYWRlcnNfY29tcGxldGUASW52YWxpZCBFT0Ygc3RhdGUAb25fcmVzZXQgcGF1c2UAb25fY2h1bmtfaGVhZGVyIHBhdXNlAG9uX21lc3NhZ2VfYmVnaW4gcGF1c2UAb25fY2h1bmtfZXh0ZW5zaW9uX3ZhbHVlIHBhdXNlAG9uX3N0YXR1c19jb21wbGV0ZSBwYXVzZQBvbl92ZXJzaW9uX2NvbXBsZXRlIHBhdXNlAG9uX3VybF9jb21wbGV0ZSBwYXVzZQBvbl9jaHVua19jb21wbGV0ZSBwYXVzZQBvbl9oZWFkZXJfdmFsdWVfY29tcGxldGUgcGF1c2UAb25fbWVzc2FnZV9jb21wbGV0ZSBwYXVzZQBvbl9tZXRob2RfY29tcGxldGUgcGF1c2UAb25faGVhZGVyX2ZpZWxkX2NvbXBsZXRlIHBhdXNlAG9uX2NodW5rX2V4dGVuc2lvbl9uYW1lIHBhdXNlAFVuZXhwZWN0ZWQgc3BhY2UgYWZ0ZXIgc3RhcnQgbGluZQBTcGFuIGNhbGxiYWNrIGVycm9yIGluIG9uX2NodW5rX2V4dGVuc2lvbl9uYW1lAEludmFsaWQgY2hhcmFjdGVyIGluIGNodW5rIGV4dGVuc2lvbnMgbmFtZQBQYXVzZSBvbiBDT05ORUNUL1VwZ3JhZGUAUGF1c2Ugb24gUFJJL1VwZ3JhZGUARXhwZWN0ZWQgSFRUUC8yIENvbm5lY3Rpb24gUHJlZmFjZQBTcGFuIGNhbGxiYWNrIGVycm9yIGluIG9uX21ldGhvZABFeHBlY3RlZCBzcGFjZSBhZnRlciBtZXRob2QAU3BhbiBjYWxsYmFjayBlcnJvciBpbiBvbl9oZWFkZXJfZmllbGQAUGF1c2VkAEludmFsaWQgd29yZCBlbmNvdW50ZXJlZABJbnZhbGlkIG1ldGhvZCBlbmNvdW50ZXJlZABVbmV4cGVjdGVkIGNoYXIgaW4gdXJsIHNjaGVtYQBSZXF1ZXN0IGhhcyBpbnZhbGlkIGBUcmFuc2Zlci1FbmNvZGluZ2AAU1dJVENIX1BST1hZAFVTRV9QUk9YWQBNS0FDVElWSVRZAFVOUFJPQ0VTU0FCTEVfRU5USVRZAENPUFkATU9WRURfUEVSTUFORU5UTFkAVE9PX0VBUkxZAE5PVElGWQBGQUlMRURfREVQRU5ERU5DWQBCQURfR0FURVdBWQBQTEFZAFBVVABDSEVDS09VVABHQVRFV0FZX1RJTUVPVVQAUkVRVUVTVF9USU1FT1VUAE5FVFdPUktfQ09OTkVDVF9USU1FT1VUAENPTk5FQ1RJT05fVElNRU9VVABMT0dJTl9USU1FT1VUAE5FVFdPUktfUkVBRF9USU1FT1VUAFBPU1QATUlTRElSRUNURURfUkVRVUVTVABDTElFTlRfQ0xPU0VEX1JFUVVFU1QAQ0xJRU5UX0NMT1NFRF9MT0FEX0JBTEFOQ0VEX1JFUVVFU1QAQkFEX1JFUVVFU1QASFRUUF9SRVFVRVNUX1NFTlRfVE9fSFRUUFNfUE9SVABSRVBPUlQASU1fQV9URUFQT1QAUkVTRVRfQ09OVEVOVABOT19DT05URU5UAFBBUlRJQUxfQ09OVEVOVABIUEVfSU5WQUxJRF9DT05TVEFOVABIUEVfQ0JfUkVTRVQAR0VUAEhQRV9TVFJJQ1QAQ09ORkxJQ1QAVEVNUE9SQVJZX1JFRElSRUNUAFBFUk1BTkVOVF9SRURJUkVDVABDT05ORUNUAE1VTFRJX1NUQVRVUwBIUEVfSU5WQUxJRF9TVEFUVVMAVE9PX01BTllfUkVRVUVTVFMARUFSTFlfSElOVFMAVU5BVkFJTEFCTEVfRk9SX0xFR0FMX1JFQVNPTlMAT1BUSU9OUwBTV0lUQ0hJTkdfUFJPVE9DT0xTAFZBUklBTlRfQUxTT19ORUdPVElBVEVTAE1VTFRJUExFX0NIT0lDRVMASU5URVJOQUxfU0VSVkVSX0VSUk9SAFdFQl9TRVJWRVJfVU5LTk9XTl9FUlJPUgBSQUlMR1VOX0VSUk9SAElERU5USVRZX1BST1ZJREVSX0FVVEhFTlRJQ0FUSU9OX0VSUk9SAFNTTF9DRVJUSUZJQ0FURV9FUlJPUgBJTlZBTElEX1hfRk9SV0FSREVEX0ZPUgBTRVRfUEFSQU1FVEVSAEdFVF9QQVJBTUVURVIASFBFX1VTRVIAU0VFX09USEVSAEhQRV9DQl9DSFVOS19IRUFERVIATUtDQUxFTkRBUgBTRVRVUABXRUJfU0VSVkVSX0lTX0RPV04AVEVBUkRPV04ASFBFX0NMT1NFRF9DT05ORUNUSU9OAEhFVVJJU1RJQ19FWFBJUkFUSU9OAERJU0NPTk5FQ1RFRF9PUEVSQVRJT04ATk9OX0FVVEhPUklUQVRJVkVfSU5GT1JNQVRJT04ASFBFX0lOVkFMSURfVkVSU0lPTgBIUEVfQ0JfTUVTU0FHRV9CRUdJTgBTSVRFX0lTX0ZST1pFTgBIUEVfSU5WQUxJRF9IRUFERVJfVE9LRU4ASU5WQUxJRF9UT0tFTgBGT1JCSURERU4ARU5IQU5DRV9ZT1VSX0NBTE0ASFBFX0lOVkFMSURfVVJMAEJMT0NLRURfQllfUEFSRU5UQUxfQ09OVFJPTABNS0NPTABBQ0wASFBFX0lOVEVSTkFMAFJFUVVFU1RfSEVBREVSX0ZJRUxEU19UT09fTEFSR0VfVU5PRkZJQ0lBTABIUEVfT0sAVU5MSU5LAFVOTE9DSwBQUkkAUkVUUllfV0lUSABIUEVfSU5WQUxJRF9DT05URU5UX0xFTkdUSABIUEVfVU5FWFBFQ1RFRF9DT05URU5UX0xFTkdUSABGTFVTSABQUk9QUEFUQ0gATS1TRUFSQ0gAVVJJX1RPT19MT05HAFBST0NFU1NJTkcATUlTQ0VMTEFORU9VU19QRVJTSVNURU5UX1dBUk5JTkcATUlTQ0VMTEFORU9VU19XQVJOSU5HAEhQRV9JTlZBTElEX1RSQU5TRkVSX0VOQ09ESU5HAEV4cGVjdGVkIENSTEYASFBFX0lOVkFMSURfQ0hVTktfU0laRQBNT1ZFAENPTlRJTlVFAEhQRV9DQl9TVEFUVVNfQ09NUExFVEUASFBFX0NCX0hFQURFUlNfQ09NUExFVEUASFBFX0NCX1ZFUlNJT05fQ09NUExFVEUASFBFX0NCX1VSTF9DT01QTEVURQBIUEVfQ0JfQ0hVTktfQ09NUExFVEUASFBFX0NCX0hFQURFUl9WQUxVRV9DT01QTEVURQBIUEVfQ0JfQ0hVTktfRVhURU5TSU9OX1ZBTFVFX0NPTVBMRVRFAEhQRV9DQl9DSFVOS19FWFRFTlNJT05fTkFNRV9DT01QTEVURQBIUEVfQ0JfTUVTU0FHRV9DT01QTEVURQBIUEVfQ0JfTUVUSE9EX0NPTVBMRVRFAEhQRV9DQl9IRUFERVJfRklFTERfQ09NUExFVEUAREVMRVRFAEhQRV9JTlZBTElEX0VPRl9TVEFURQBJTlZBTElEX1NTTF9DRVJUSUZJQ0FURQBQQVVTRQBOT19SRVNQT05TRQBVTlNVUFBPUlRFRF9NRURJQV9UWVBFAEdPTkUATk9UX0FDQ0VQVEFCTEUAU0VSVklDRV9VTkFWQUlMQUJMRQBSQU5HRV9OT1RfU0FUSVNGSUFCTEUAT1JJR0lOX0lTX1VOUkVBQ0hBQkxFAFJFU1BPTlNFX0lTX1NUQUxFAFBVUkdFAE1FUkdFAFJFUVVFU1RfSEVBREVSX0ZJRUxEU19UT09fTEFSR0UAUkVRVUVTVF9IRUFERVJfVE9PX0xBUkdFAFBBWUxPQURfVE9PX0xBUkdFAElOU1VGRklDSUVOVF9TVE9SQUdFAEhQRV9QQVVTRURfVVBHUkFERQBIUEVfUEFVU0VEX0gyX1VQR1JBREUAU09VUkNFAEFOTk9VTkNFAFRSQUNFAEhQRV9VTkVYUEVDVEVEX1NQQUNFAERFU0NSSUJFAFVOU1VCU0NSSUJFAFJFQ09SRABIUEVfSU5WQUxJRF9NRVRIT0QATk9UX0ZPVU5EAFBST1BGSU5EAFVOQklORABSRUJJTkQAVU5BVVRIT1JJWkVEAE1FVEhPRF9OT1RfQUxMT1dFRABIVFRQX1ZFUlNJT05fTk9UX1NVUFBPUlRFRABBTFJFQURZX1JFUE9SVEVEAEFDQ0VQVEVEAE5PVF9JTVBMRU1FTlRFRABMT09QX0RFVEVDVEVEAEhQRV9DUl9FWFBFQ1RFRABIUEVfTEZfRVhQRUNURUQAQ1JFQVRFRABJTV9VU0VEAEhQRV9QQVVTRUQAVElNRU9VVF9PQ0NVUkVEAFBBWU1FTlRfUkVRVUlSRUQAUFJFQ09ORElUSU9OX1JFUVVJUkVEAFBST1hZX0FVVEhFTlRJQ0FUSU9OX1JFUVVJUkVEAE5FVFdPUktfQVVUSEVOVElDQVRJT05fUkVRVUlSRUQATEVOR1RIX1JFUVVJUkVEAFNTTF9DRVJUSUZJQ0FURV9SRVFVSVJFRABVUEdSQURFX1JFUVVJUkVEAFBBR0VfRVhQSVJFRABQUkVDT05ESVRJT05fRkFJTEVEAEVYUEVDVEFUSU9OX0ZBSUxFRABSRVZBTElEQVRJT05fRkFJTEVEAFNTTF9IQU5EU0hBS0VfRkFJTEVEAExPQ0tFRABUUkFOU0ZPUk1BVElPTl9BUFBMSUVEAE5PVF9NT0RJRklFRABOT1RfRVhURU5ERUQAQkFORFdJRFRIX0xJTUlUX0VYQ0VFREVEAFNJVEVfSVNfT1ZFUkxPQURFRABIRUFEAEV4cGVjdGVkIEhUVFAvAABeEwAAJhMAADAQAADwFwAAnRMAABUSAAA5FwAA8BIAAAoQAAB1EgAArRIAAIITAABPFAAAfxAAAKAVAAAjFAAAiRIAAIsUAABNFQAA1BEAAM8UAAAQGAAAyRYAANwWAADBEQAA4BcAALsUAAB0FAAAfBUAAOUUAAAIFwAAHxAAAGUVAACjFAAAKBUAAAIVAACZFQAALBAAAIsZAABPDwAA1A4AAGoQAADOEAAAAhcAAIkOAABuEwAAHBMAAGYUAABWFwAAwRMAAM0TAABsEwAAaBcAAGYXAABfFwAAIhMAAM4PAABpDgAA2A4AAGMWAADLEwAAqg4AACgXAAAmFwAAxRMAAF0WAADoEQAAZxMAAGUTAADyFgAAcxMAAB0XAAD5FgAA8xEAAM8OAADOFQAADBIAALMRAAClEQAAYRAAADIXAAC7EwBB+TULAQEAQZA2C+ABAQECAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAQf03CwEBAEGROAteAgMCAgICAgAAAgIAAgIAAgICAgICAgICAgAEAAAAAAACAgICAgICAgICAgICAgICAgICAgICAgICAgAAAAICAgICAgICAgICAgICAgICAgICAgICAgICAgICAAIAAgBB/TkLAQEAQZE6C14CAAICAgICAAACAgACAgACAgICAgICAgICAAMABAAAAAICAgICAgICAgICAgICAgICAgICAgICAgICAAAAAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAAgACAEHwOwsNbG9zZWVlcC1hbGl2ZQBBiTwLAQEAQaA8C+ABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAQYk+CwEBAEGgPgvnAQEBAQEBAQEBAQEBAQIBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBY2h1bmtlZABBsMAAC18BAQABAQEBAQAAAQEAAQEAAQEBAQEBAQEBAQAAAAAAAAABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQAAAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAEAAQBBkMIACyFlY3Rpb25lbnQtbGVuZ3Rob25yb3h5LWNvbm5lY3Rpb24AQcDCAAstcmFuc2Zlci1lbmNvZGluZ3BncmFkZQ0KDQoNClNNDQoNClRUUC9DRS9UU1AvAEH5wgALBQECAAEDAEGQwwAL4AEEAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQBB+cQACwUBAgABAwBBkMUAC+ABBAEBBQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAQfnGAAsEAQAAAQBBkccAC98BAQEAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQBB+sgACwQBAAACAEGQyQALXwMEAAAEBAQEBAQEBAQEBAUEBAQEBAQEBAQEBAQABAAGBwQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAEAAQABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQAAAAEAEH6ygALBAEAAAEAQZDLAAsBAQBBqssAC0ECAAAAAAAAAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMAAAAAAAADAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwBB+swACwQBAAABAEGQzQALAQEAQZrNAAsGAgAAAAACAEGxzQALOgMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAAAAAAAAAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMAQfDOAAuWAU5PVU5DRUVDS09VVE5FQ1RFVEVDUklCRUxVU0hFVEVBRFNFQVJDSFJHRUNUSVZJVFlMRU5EQVJWRU9USUZZUFRJT05TQ0hTRUFZU1RBVENIR0VPUkRJUkVDVE9SVFJDSFBBUkFNRVRFUlVSQ0VCU0NSSUJFQVJET1dOQUNFSU5ETktDS1VCU0NSSUJFSFRUUC9BRFRQLw==", "base64"); } }); -// .yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/web/fetch/constants.js +// .yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/web/fetch/constants.js var require_constants4 = __commonJS({ - ".yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/web/fetch/constants.js"(exports2, module2) { + ".yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/web/fetch/constants.js"(exports2, module2) { "use strict"; var corsSafeListedMethods = ( /** @type {const} */ @@ -7246,9 +7410,9 @@ var require_constants4 = __commonJS({ } }); -// .yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/web/fetch/global.js +// .yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/web/fetch/global.js var require_global = __commonJS({ - ".yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/web/fetch/global.js"(exports2, module2) { + ".yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/web/fetch/global.js"(exports2, module2) { "use strict"; var globalOrigin = Symbol.for("undici.globalOrigin.1"); function getGlobalOrigin() { @@ -7282,9 +7446,9 @@ var require_global = __commonJS({ } }); -// .yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/web/fetch/data-url.js +// .yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/web/fetch/data-url.js var require_data_url = __commonJS({ - ".yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/web/fetch/data-url.js"(exports2, module2) { + ".yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/web/fetch/data-url.js"(exports2, module2) { "use strict"; var assert5 = require("node:assert"); var encoder = new TextEncoder(); @@ -7634,9 +7798,9 @@ var require_data_url = __commonJS({ } }); -// .yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/web/fetch/webidl.js +// .yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/web/fetch/webidl.js var require_webidl = __commonJS({ - ".yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/web/fetch/webidl.js"(exports2, module2) { + ".yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/web/fetch/webidl.js"(exports2, module2) { "use strict"; var { types, inspect } = require("node:util"); var { markAsUncloneable } = require("node:worker_threads"); @@ -7839,8 +8003,8 @@ var require_webidl = __commonJS({ } const keys = Reflect.ownKeys(O); for (const key of keys) { - const desc = Reflect.getOwnPropertyDescriptor(O, key); - if (desc?.enumerable) { + const desc2 = Reflect.getOwnPropertyDescriptor(O, key); + if (desc2?.enumerable) { const typedKey = keyConverter(key, prefix, argument); const typedValue = valueConverter(O[key], prefix, argument); result[typedKey] = typedValue; @@ -8053,9 +8217,9 @@ var require_webidl = __commonJS({ } }); -// .yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/web/fetch/util.js +// .yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/web/fetch/util.js var require_util3 = __commonJS({ - ".yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/web/fetch/util.js"(exports2, module2) { + ".yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/web/fetch/util.js"(exports2, module2) { "use strict"; var { Transform } = require("node:stream"); var zlib = require("node:zlib"); @@ -8453,8 +8617,8 @@ var require_util3 = __commonJS({ function createDeferredPromise() { let res; let rej; - const promise = new Promise((resolve2, reject) => { - res = resolve2; + const promise = new Promise((resolve, reject) => { + res = resolve; rej = reject; }); return { promise, resolve: res, reject: rej }; @@ -8923,9 +9087,9 @@ var require_util3 = __commonJS({ } }); -// .yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/web/fetch/symbols.js +// .yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/web/fetch/symbols.js var require_symbols2 = __commonJS({ - ".yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/web/fetch/symbols.js"(exports2, module2) { + ".yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/web/fetch/symbols.js"(exports2, module2) { "use strict"; module2.exports = { kUrl: Symbol("url"), @@ -8937,9 +9101,9 @@ var require_symbols2 = __commonJS({ } }); -// .yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/web/fetch/file.js +// .yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/web/fetch/file.js var require_file = __commonJS({ - ".yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/web/fetch/file.js"(exports2, module2) { + ".yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/web/fetch/file.js"(exports2, module2) { "use strict"; var { Blob: Blob2, File } = require("node:buffer"); var { kState } = require_symbols2(); @@ -9000,9 +9164,9 @@ var require_file = __commonJS({ } }); -// .yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/web/fetch/formdata.js +// .yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/web/fetch/formdata.js var require_formdata = __commonJS({ - ".yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/web/fetch/formdata.js"(exports2, module2) { + ".yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/web/fetch/formdata.js"(exports2, module2) { "use strict"; var { isBlobLike, iteratorMixin } = require_util3(); var { kState } = require_symbols2(); @@ -9147,9 +9311,9 @@ var require_formdata = __commonJS({ } }); -// .yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/web/fetch/formdata-parser.js +// .yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/web/fetch/formdata-parser.js var require_formdata_parser = __commonJS({ - ".yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/web/fetch/formdata-parser.js"(exports2, module2) { + ".yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/web/fetch/formdata-parser.js"(exports2, module2) { "use strict"; var { isUSVString, bufferToLowerCasedHeaderName } = require_util(); var { utf8DecodeBytes } = require_util3(); @@ -9398,9 +9562,9 @@ var require_formdata_parser = __commonJS({ } }); -// .yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/web/fetch/body.js +// .yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/web/fetch/body.js var require_body = __commonJS({ - ".yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/web/fetch/body.js"(exports2, module2) { + ".yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/web/fetch/body.js"(exports2, module2) { "use strict"; var util = require_util(); var { @@ -9430,7 +9594,7 @@ var require_body = __commonJS({ random = (max) => Math.floor(Math.random(max)); } var textEncoder = new TextEncoder(); - function noop2() { + function noop3() { } var hasFinalizationRegistry = globalThis.FinalizationRegistry && process.version.indexOf("v18") !== 0; var streamRegistry; @@ -9438,7 +9602,7 @@ var require_body = __commonJS({ streamRegistry = new FinalizationRegistry((weakRef) => { const stream = weakRef.deref(); if (stream && !stream.locked && !isDisturbed(stream) && !isErrored(stream)) { - stream.cancel("Response object has been garbage collected").catch(noop2); + stream.cancel("Response object has been garbage collected").catch(noop3); } }); } @@ -9508,7 +9672,8 @@ Content-Type: ${value.type || "application/octet-stream"}\r } } } - const chunk = textEncoder.encode(`--${boundary}--`); + const chunk = textEncoder.encode(`--${boundary}--\r +`); blobParts.push(chunk); length += chunk.byteLength; if (hasUnknownSizeValue) { @@ -9586,9 +9751,6 @@ Content-Type: ${value.type || "application/octet-stream"}\r } function cloneBody(instance, body) { const [out1, out2] = body.stream.tee(); - if (hasFinalizationRegistry) { - streamRegistry.register(instance, new WeakRef(out1)); - } body.stream = out1; return { stream: out2, @@ -9714,9 +9876,9 @@ Content-Type: ${value.type || "application/octet-stream"}\r } }); -// .yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/dispatcher/client-h1.js +// .yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/dispatcher/client-h1.js var require_client_h1 = __commonJS({ - ".yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/dispatcher/client-h1.js"(exports2, module2) { + ".yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/dispatcher/client-h1.js"(exports2, module2) { "use strict"; var assert5 = require("node:assert"); var util = require_util(); @@ -10597,12 +10759,12 @@ upgrade: ${upgrade}\r cb(); } } - const waitForDrain = () => new Promise((resolve2, reject) => { + const waitForDrain = () => new Promise((resolve, reject) => { assert5(callback === null); if (socket[kError]) { reject(socket[kError]); } else { - callback = resolve2; + callback = resolve; } }); socket.on("close", onDrain).on("drain", onDrain); @@ -10734,9 +10896,9 @@ ${len.toString(16)}\r } }); -// .yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/dispatcher/client-h2.js +// .yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/dispatcher/client-h2.js var require_client_h2 = __commonJS({ - ".yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/dispatcher/client-h2.js"(exports2, module2) { + ".yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/dispatcher/client-h2.js"(exports2, module2) { "use strict"; var assert5 = require("node:assert"); var { pipeline } = require("node:stream"); @@ -11239,12 +11401,12 @@ var require_client_h2 = __commonJS({ cb(); } } - const waitForDrain = () => new Promise((resolve2, reject) => { + const waitForDrain = () => new Promise((resolve, reject) => { assert5(callback === null); if (socket[kError]) { reject(socket[kError]); } else { - callback = resolve2; + callback = resolve; } }); h2stream.on("close", onDrain).on("drain", onDrain); @@ -11275,9 +11437,9 @@ var require_client_h2 = __commonJS({ } }); -// .yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/handler/redirect-handler.js +// .yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/handler/redirect-handler.js var require_redirect_handler = __commonJS({ - ".yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/handler/redirect-handler.js"(exports2, module2) { + ".yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/handler/redirect-handler.js"(exports2, module2) { "use strict"; var util = require_util(); var { kBodyUsed } = require_symbols(); @@ -11434,9 +11596,9 @@ var require_redirect_handler = __commonJS({ } }); -// .yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/interceptor/redirect-interceptor.js +// .yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/interceptor/redirect-interceptor.js var require_redirect_interceptor = __commonJS({ - ".yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/interceptor/redirect-interceptor.js"(exports2, module2) { + ".yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/interceptor/redirect-interceptor.js"(exports2, module2) { "use strict"; var RedirectHandler = require_redirect_handler(); function createRedirectInterceptor({ maxRedirections: defaultMaxRedirections }) { @@ -11456,9 +11618,9 @@ var require_redirect_interceptor = __commonJS({ } }); -// .yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/dispatcher/client.js +// .yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/dispatcher/client.js var require_client = __commonJS({ - ".yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/dispatcher/client.js"(exports2, module2) { + ".yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/dispatcher/client.js"(exports2, module2) { "use strict"; var assert5 = require("node:assert"); var net = require("node:net"); @@ -11519,7 +11681,7 @@ var require_client = __commonJS({ var connectH2 = require_client_h2(); var deprecatedInterceptorWarned = false; var kClosedResolve = Symbol("kClosedResolve"); - var noop2 = () => { + var noop3 = () => { }; function getPipelining(client) { return client[kPipelining] ?? client[kHTTPContext]?.defaultPipelining ?? 1; @@ -11721,16 +11883,16 @@ var require_client = __commonJS({ return this[kNeedDrain] < 2; } async [kClose]() { - return new Promise((resolve2) => { + return new Promise((resolve) => { if (this[kSize]) { - this[kClosedResolve] = resolve2; + this[kClosedResolve] = resolve; } else { - resolve2(null); + resolve(null); } }); } async [kDestroy](err) { - return new Promise((resolve2) => { + return new Promise((resolve) => { const requests = this[kQueue].splice(this[kPendingIdx]); for (let i = 0; i < requests.length; i++) { const request = requests[i]; @@ -11741,7 +11903,7 @@ var require_client = __commonJS({ this[kClosedResolve](); this[kClosedResolve] = null; } - resolve2(null); + resolve(null); }; if (this[kHTTPContext]) { this[kHTTPContext].destroy(err, callback); @@ -11792,7 +11954,7 @@ var require_client = __commonJS({ }); } try { - const socket = await new Promise((resolve2, reject) => { + const socket = await new Promise((resolve, reject) => { client[kConnector]({ host, hostname, @@ -11804,19 +11966,19 @@ var require_client = __commonJS({ if (err) { reject(err); } else { - resolve2(socket2); + resolve(socket2); } }); }); if (client.destroyed) { - util.destroy(socket.on("error", noop2), new ClientDestroyedError()); + util.destroy(socket.on("error", noop3), new ClientDestroyedError()); return; } assert5(socket); try { client[kHTTPContext] = socket.alpnProtocol === "h2" ? await connectH2(client, socket) : await connectH1(client, socket); } catch (err) { - socket.destroy().on("error", noop2); + socket.destroy().on("error", noop3); throw err; } client[kConnecting] = false; @@ -11956,9 +12118,9 @@ var require_client = __commonJS({ } }); -// .yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/dispatcher/pool.js +// .yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/dispatcher/pool.js var require_pool = __commonJS({ - ".yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/dispatcher/pool.js"(exports2, module2) { + ".yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/dispatcher/pool.js"(exports2, module2) { "use strict"; var { PoolBase, @@ -12021,6 +12183,14 @@ var require_pool = __commonJS({ this[kOptions] = { ...util.deepClone(options), connect, allowH2 }; this[kOptions].interceptors = options.interceptors ? { ...options.interceptors } : void 0; this[kFactory] = factory; + this.on("connectionError", (origin2, targets, error) => { + for (const target of targets) { + const idx = this[kClients].indexOf(target); + if (idx !== -1) { + this[kClients].splice(idx, 1); + } + } + }); } [kGetDispatcher]() { for (const client of this[kClients]) { @@ -12039,9 +12209,9 @@ var require_pool = __commonJS({ } }); -// .yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/dispatcher/agent.js +// .yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/dispatcher/agent.js var require_agent = __commonJS({ - ".yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/dispatcher/agent.js"(exports2, module2) { + ".yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/dispatcher/agent.js"(exports2, module2) { "use strict"; var { InvalidArgumentError } = require_errors(); var { kClients, kRunning, kClose, kDestroy, kDispatch, kInterceptors } = require_symbols(); @@ -12136,30 +12306,83 @@ var require_agent = __commonJS({ } }); -// .yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/dispatcher/proxy-agent.js +// .yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/dispatcher/proxy-agent.js var require_proxy_agent = __commonJS({ - ".yarn/cache/undici-npm-6.21.1-0f7fc2c179-d604080e4f.zip/node_modules/undici/lib/dispatcher/proxy-agent.js"(exports2, module2) { + ".yarn/cache/undici-npm-6.22.0-4664dd0314-47903c489d.zip/node_modules/undici/lib/dispatcher/proxy-agent.js"(exports2, module2) { "use strict"; - var { kProxy, kClose, kDestroy, kInterceptors } = require_symbols(); + var { kProxy, kClose, kDestroy, kDispatch, kInterceptors } = require_symbols(); var { URL: URL2 } = require("node:url"); var Agent = require_agent(); var Pool = require_pool(); var DispatcherBase = require_dispatcher_base(); var { InvalidArgumentError, RequestAbortedError, SecureProxyConnectionError } = require_errors(); var buildConnector = require_connect(); + var Client = require_client(); var kAgent = Symbol("proxy agent"); var kClient = Symbol("proxy client"); var kProxyHeaders = Symbol("proxy headers"); var kRequestTls = Symbol("request tls settings"); var kProxyTls = Symbol("proxy tls settings"); var kConnectEndpoint = Symbol("connect endpoint function"); + var kTunnelProxy = Symbol("tunnel proxy"); function defaultProtocolPort(protocol) { return protocol === "https:" ? 443 : 80; } function defaultFactory(origin, opts) { return new Pool(origin, opts); } - var noop2 = () => { + var noop3 = () => { + }; + function defaultAgentFactory(origin, opts) { + if (opts.connections === 1) { + return new Client(origin, opts); + } + return new Pool(origin, opts); + } + var Http1ProxyWrapper = class extends DispatcherBase { + #client; + constructor(proxyUrl, { headers = {}, connect, factory }) { + super(); + if (!proxyUrl) { + throw new InvalidArgumentError("Proxy URL is mandatory"); + } + this[kProxyHeaders] = headers; + if (factory) { + this.#client = factory(proxyUrl, { connect }); + } else { + this.#client = new Client(proxyUrl, { connect }); + } + } + [kDispatch](opts, handler) { + const onHeaders = handler.onHeaders; + handler.onHeaders = function(statusCode, data, resume) { + if (statusCode === 407) { + if (typeof handler.onError === "function") { + handler.onError(new InvalidArgumentError("Proxy Authentication Required (407)")); + } + return; + } + if (onHeaders) onHeaders.call(this, statusCode, data, resume); + }; + const { + origin, + path: path16 = "/", + headers = {} + } = opts; + opts.path = origin + path16; + if (!("host" in headers) && !("Host" in headers)) { + const { host } = new URL2(origin); + headers.host = host; + } + opts.headers = { ...this[kProxyHeaders], ...headers }; + return this.#client[kDispatch](opts, handler); + } + async [kClose]() { + return this.#client.close(); + } + async [kDestroy](err) { + return this.#client.destroy(err); + } }; var ProxyAgent2 = class extends DispatcherBase { constructor(opts) { @@ -12171,6 +12394,7 @@ var require_proxy_agent = __commonJS({ if (typeof clientFactory !== "function") { throw new InvalidArgumentError("Proxy opts.clientFactory must be a function."); } + const { proxyTunnel = true } = opts; const url = this.#getUrl(opts); const { href, origin, port, protocol, username, password, hostname: proxyHostname } = url; this[kProxy] = { uri: href, protocol }; @@ -12178,6 +12402,7 @@ var require_proxy_agent = __commonJS({ this[kRequestTls] = opts.requestTls; this[kProxyTls] = opts.proxyTls; this[kProxyHeaders] = opts.headers || {}; + this[kTunnelProxy] = proxyTunnel; if (opts.auth && opts.token) { throw new InvalidArgumentError("opts.auth cannot be used in combination with opts.token"); } else if (opts.auth) { @@ -12189,9 +12414,22 @@ var require_proxy_agent = __commonJS({ } const connect = buildConnector({ ...opts.proxyTls }); this[kConnectEndpoint] = buildConnector({ ...opts.requestTls }); + const agentFactory = opts.factory || defaultAgentFactory; + const factory = (origin2, options) => { + const { protocol: protocol2 } = new URL2(origin2); + if (!this[kTunnelProxy] && protocol2 === "http:" && this[kProxy].protocol === "http:") { + return new Http1ProxyWrapper(this[kProxy].uri, { + headers: this[kProxyHeaders], + connect, + factory: agentFactory + }); + } + return agentFactory(origin2, options); + }; this[kClient] = clientFactory(url, { connect }); this[kAgent] = new Agent({ ...opts, + factory, connect: async (opts2, callback) => { let requestedPath = opts2.host; if (!opts2.port) { @@ -12210,7 +12448,7 @@ var require_proxy_agent = __commonJS({ servername: this[kProxyTls]?.servername || proxyHostname }); if (statusCode !== 200) { - socket.on("error", noop2).destroy(); + socket.on("error", noop3).destroy(); callback(new RequestAbortedError(`Proxy response (${statusCode}) !== 200 when HTTP Tunneling`)); } if (opts2.protocol !== "https:") { @@ -13023,10 +13261,10 @@ var init_esm = __esm({ * Return a void Promise that resolves once the stream ends. */ async promise() { - return new Promise((resolve2, reject) => { + return new Promise((resolve, reject) => { this.on(DESTROYED, () => reject(new Error("stream destroyed"))); this.on("error", (er) => reject(er)); - this.on("end", () => resolve2()); + this.on("end", () => resolve()); }); } /** @@ -13050,7 +13288,7 @@ var init_esm = __esm({ return Promise.resolve({ done: false, value: res }); if (this[EOF]) return stop(); - let resolve2; + let resolve; let reject; const onerr = (er) => { this.off("data", ondata); @@ -13064,19 +13302,19 @@ var init_esm = __esm({ this.off("end", onend); this.off(DESTROYED, ondestroy); this.pause(); - resolve2({ value, done: !!this[EOF] }); + resolve({ value, done: !!this[EOF] }); }; const onend = () => { this.off("error", onerr); this.off("data", ondata); this.off(DESTROYED, ondestroy); stop(); - resolve2({ done: true, value: void 0 }); + resolve({ done: true, value: void 0 }); }; const ondestroy = () => onerr(new Error("stream destroyed")); return new Promise((res2, rej) => { reject = rej; - resolve2 = res2; + resolve = res2; this.once(DESTROYED, ondestroy); this.once("error", onerr); this.once("end", onend); @@ -13562,10 +13800,10 @@ var init_esm2 = __esm({ } }); -// .yarn/cache/tar-npm-7.4.3-1dbbd1ffc3-d4679609bb.zip/node_modules/tar/dist/esm/options.js +// .yarn/cache/tar-npm-7.5.1-7b414f7fec-0dad0596a6.zip/node_modules/tar/dist/esm/options.js var argmap, isSyncFile, isAsyncFile, isSyncNoFile, isAsyncNoFile, dealiasKey, dealias; var init_options = __esm({ - ".yarn/cache/tar-npm-7.4.3-1dbbd1ffc3-d4679609bb.zip/node_modules/tar/dist/esm/options.js"() { + ".yarn/cache/tar-npm-7.5.1-7b414f7fec-0dad0596a6.zip/node_modules/tar/dist/esm/options.js"() { argmap = /* @__PURE__ */ new Map([ ["C", "cwd"], ["f", "file"], @@ -13615,10 +13853,10 @@ var init_options = __esm({ } }); -// .yarn/cache/tar-npm-7.4.3-1dbbd1ffc3-d4679609bb.zip/node_modules/tar/dist/esm/make-command.js +// .yarn/cache/tar-npm-7.5.1-7b414f7fec-0dad0596a6.zip/node_modules/tar/dist/esm/make-command.js var makeCommand; var init_make_command = __esm({ - ".yarn/cache/tar-npm-7.4.3-1dbbd1ffc3-d4679609bb.zip/node_modules/tar/dist/esm/make-command.js"() { + ".yarn/cache/tar-npm-7.5.1-7b414f7fec-0dad0596a6.zip/node_modules/tar/dist/esm/make-command.js"() { init_options(); makeCommand = (syncFile, asyncFile, syncNoFile, asyncNoFile, validate) => { return Object.assign((opt_ = [], entries, cb) => { @@ -13670,10 +13908,10 @@ var init_make_command = __esm({ } }); -// .yarn/cache/minizlib-npm-3.0.1-4bdabd978f-82f8bf70da.zip/node_modules/minizlib/dist/esm/constants.js +// .yarn/cache/minizlib-npm-3.1.0-6680befdba-5aad75ab00.zip/node_modules/minizlib/dist/esm/constants.js var import_zlib, realZlibConstants, constants; var init_constants = __esm({ - ".yarn/cache/minizlib-npm-3.0.1-4bdabd978f-82f8bf70da.zip/node_modules/minizlib/dist/esm/constants.js"() { + ".yarn/cache/minizlib-npm-3.1.0-6680befdba-5aad75ab00.zip/node_modules/minizlib/dist/esm/constants.js"() { import_zlib = __toESM(require("zlib"), 1); realZlibConstants = import_zlib.default.constants || { ZLIB_VERNUM: 4736 }; constants = Object.freeze(Object.assign(/* @__PURE__ */ Object.create(null), { @@ -13787,29 +14025,35 @@ var init_constants = __esm({ } }); -// .yarn/cache/minizlib-npm-3.0.1-4bdabd978f-82f8bf70da.zip/node_modules/minizlib/dist/esm/index.js -var import_assert2, import_buffer, import_zlib2, OriginalBufferConcat, _superWrite, ZlibError, _flushFlag, ZlibBase, Zlib, Gzip, Unzip, Brotli, BrotliCompress, BrotliDecompress; +// .yarn/cache/minizlib-npm-3.1.0-6680befdba-5aad75ab00.zip/node_modules/minizlib/dist/esm/index.js +var import_assert2, import_buffer, realZlib2, OriginalBufferConcat, desc, noop, passthroughBufferConcat, _superWrite, ZlibError, _flushFlag, ZlibBase, Zlib, Gzip, Unzip, Brotli, BrotliCompress, BrotliDecompress, Zstd, ZstdCompress, ZstdDecompress; var init_esm3 = __esm({ - ".yarn/cache/minizlib-npm-3.0.1-4bdabd978f-82f8bf70da.zip/node_modules/minizlib/dist/esm/index.js"() { + ".yarn/cache/minizlib-npm-3.1.0-6680befdba-5aad75ab00.zip/node_modules/minizlib/dist/esm/index.js"() { import_assert2 = __toESM(require("assert"), 1); import_buffer = require("buffer"); init_esm(); - import_zlib2 = __toESM(require("zlib"), 1); + realZlib2 = __toESM(require("zlib"), 1); init_constants(); init_constants(); OriginalBufferConcat = import_buffer.Buffer.concat; + desc = Object.getOwnPropertyDescriptor(import_buffer.Buffer, "concat"); + noop = (args) => args; + passthroughBufferConcat = desc?.writable === true || desc?.set !== void 0 ? (makeNoOp) => { + import_buffer.Buffer.concat = makeNoOp ? noop : OriginalBufferConcat; + } : (_) => { + }; _superWrite = Symbol("_superWrite"); ZlibError = class extends Error { code; errno; - constructor(err) { - super("zlib: " + err.message); + constructor(err, origin) { + super("zlib: " + err.message, { cause: err }); this.code = err.code; this.errno = err.errno; if (!this.code) this.code = "ZLIB_ERROR"; this.message = "zlib: " + err.message; - Error.captureStackTrace(this, this.constructor); + Error.captureStackTrace(this, origin ?? this.constructor); } get name() { return "ZlibError"; @@ -13842,10 +14086,13 @@ var init_esm3 = __esm({ this.#flushFlag = opts.flush ?? 0; this.#finishFlushFlag = opts.finishFlush ?? 0; this.#fullFlushFlag = opts.fullFlushFlag ?? 0; + if (typeof realZlib2[mode] !== "function") { + throw new TypeError("Compression method not supported: " + mode); + } try { - this.#handle = new import_zlib2.default[mode](opts); + this.#handle = new realZlib2[mode](opts); } catch (er) { - throw new ZlibError(er); + throw new ZlibError(er, this.constructor); } this.#onError = (err) => { if (this.#sawError) @@ -13919,15 +14166,15 @@ var init_esm3 = __esm({ const originalClose = this.#handle.close; this.#handle.close = () => { }; - import_buffer.Buffer.concat = (args) => args; + passthroughBufferConcat(true); let result = void 0; try { const flushFlag = typeof chunk[_flushFlag] === "number" ? chunk[_flushFlag] : this.#flushFlag; result = this.#handle._processChunk(chunk, flushFlag); - import_buffer.Buffer.concat = OriginalBufferConcat; + passthroughBufferConcat(false); } catch (err) { - import_buffer.Buffer.concat = OriginalBufferConcat; - this.#onError(new ZlibError(err)); + passthroughBufferConcat(false); + this.#onError(new ZlibError(err, this.write)); } finally { if (this.#handle) { ; @@ -13938,7 +14185,7 @@ var init_esm3 = __esm({ } } if (this.#handle) - this.#handle.on("error", (er) => this.#onError(new ZlibError(er))); + this.#handle.on("error", (er) => this.#onError(new ZlibError(er, this.write))); let writeReturn; if (result) { if (Array.isArray(result) && result.length > 0) { @@ -14038,1382 +14285,1041 @@ var init_esm3 = __esm({ super(opts, "BrotliDecompress"); } }; + Zstd = class extends ZlibBase { + constructor(opts, mode) { + opts = opts || {}; + opts.flush = opts.flush || constants.ZSTD_e_continue; + opts.finishFlush = opts.finishFlush || constants.ZSTD_e_end; + opts.fullFlushFlag = constants.ZSTD_e_flush; + super(opts, mode); + } + }; + ZstdCompress = class extends Zstd { + constructor(opts) { + super(opts, "ZstdCompress"); + } + }; + ZstdDecompress = class extends Zstd { + constructor(opts) { + super(opts, "ZstdDecompress"); + } + }; } }); -// .yarn/cache/yallist-npm-5.0.0-8732dd9f1c-a499c81ce6.zip/node_modules/yallist/dist/esm/index.js -function insertAfter(self2, node, value) { - const prev = node; - const next = node ? node.next : self2.head; - const inserted = new Node(value, prev, next, self2); - if (inserted.next === void 0) { - self2.tail = inserted; - } - if (inserted.prev === void 0) { - self2.head = inserted; - } - self2.length++; - return inserted; -} -function push(self2, item) { - self2.tail = new Node(item, self2.tail, void 0, self2); - if (!self2.head) { - self2.head = self2.tail; - } - self2.length++; -} -function unshift(self2, item) { - self2.head = new Node(item, void 0, self2.head, self2); - if (!self2.tail) { - self2.tail = self2.head; - } - self2.length++; -} -var Yallist, Node; -var init_esm4 = __esm({ - ".yarn/cache/yallist-npm-5.0.0-8732dd9f1c-a499c81ce6.zip/node_modules/yallist/dist/esm/index.js"() { - Yallist = class _Yallist { - tail; - head; - length = 0; - static create(list2 = []) { - return new _Yallist(list2); +// .yarn/cache/tar-npm-7.5.1-7b414f7fec-0dad0596a6.zip/node_modules/tar/dist/esm/large-numbers.js +var encode, encodePositive, encodeNegative, parse, twos, pos, onesComp, twosComp; +var init_large_numbers = __esm({ + ".yarn/cache/tar-npm-7.5.1-7b414f7fec-0dad0596a6.zip/node_modules/tar/dist/esm/large-numbers.js"() { + encode = (num, buf) => { + if (!Number.isSafeInteger(num)) { + throw Error("cannot encode number outside of javascript safe integer range"); + } else if (num < 0) { + encodeNegative(num, buf); + } else { + encodePositive(num, buf); } - constructor(list2 = []) { - for (const item of list2) { - this.push(item); - } + return buf; + }; + encodePositive = (num, buf) => { + buf[0] = 128; + for (var i = buf.length; i > 1; i--) { + buf[i - 1] = num & 255; + num = Math.floor(num / 256); } - *[Symbol.iterator]() { - for (let walker = this.head; walker; walker = walker.next) { - yield walker.value; + }; + encodeNegative = (num, buf) => { + buf[0] = 255; + var flipped = false; + num = num * -1; + for (var i = buf.length; i > 1; i--) { + var byte = num & 255; + num = Math.floor(num / 256); + if (flipped) { + buf[i - 1] = onesComp(byte); + } else if (byte === 0) { + buf[i - 1] = 0; + } else { + flipped = true; + buf[i - 1] = twosComp(byte); } } - removeNode(node) { - if (node.list !== this) { - throw new Error("removing node which does not belong to this list"); - } - const next = node.next; - const prev = node.prev; - if (next) { - next.prev = prev; - } - if (prev) { - prev.next = next; - } - if (node === this.head) { - this.head = next; - } - if (node === this.tail) { - this.tail = prev; - } - this.length--; - node.next = void 0; - node.prev = void 0; - node.list = void 0; - return next; + }; + parse = (buf) => { + const pre = buf[0]; + const value = pre === 128 ? pos(buf.subarray(1, buf.length)) : pre === 255 ? twos(buf) : null; + if (value === null) { + throw Error("invalid base256 encoding"); } - unshiftNode(node) { - if (node === this.head) { - return; - } - if (node.list) { - node.list.removeNode(node); - } - const head = this.head; - node.list = this; - node.next = head; - if (head) { - head.prev = node; + if (!Number.isSafeInteger(value)) { + throw Error("parsed number outside of javascript safe integer range"); + } + return value; + }; + twos = (buf) => { + var len = buf.length; + var sum = 0; + var flipped = false; + for (var i = len - 1; i > -1; i--) { + var byte = Number(buf[i]); + var f; + if (flipped) { + f = onesComp(byte); + } else if (byte === 0) { + f = byte; + } else { + flipped = true; + f = twosComp(byte); } - this.head = node; - if (!this.tail) { - this.tail = node; + if (f !== 0) { + sum -= f * Math.pow(256, len - i - 1); } - this.length++; } - pushNode(node) { - if (node === this.tail) { - return; - } - if (node.list) { - node.list.removeNode(node); - } - const tail = this.tail; - node.list = this; - node.prev = tail; - if (tail) { - tail.next = node; - } - this.tail = node; - if (!this.head) { - this.head = node; + return sum; + }; + pos = (buf) => { + var len = buf.length; + var sum = 0; + for (var i = len - 1; i > -1; i--) { + var byte = Number(buf[i]); + if (byte !== 0) { + sum += byte * Math.pow(256, len - i - 1); } - this.length++; } - push(...args) { - for (let i = 0, l = args.length; i < l; i++) { - push(this, args[i]); + return sum; + }; + onesComp = (byte) => (255 ^ byte) & 255; + twosComp = (byte) => (255 ^ byte) + 1 & 255; + } +}); + +// .yarn/cache/tar-npm-7.5.1-7b414f7fec-0dad0596a6.zip/node_modules/tar/dist/esm/types.js +var isCode, name, code; +var init_types = __esm({ + ".yarn/cache/tar-npm-7.5.1-7b414f7fec-0dad0596a6.zip/node_modules/tar/dist/esm/types.js"() { + isCode = (c) => name.has(c); + name = /* @__PURE__ */ new Map([ + ["0", "File"], + // same as File + ["", "OldFile"], + ["1", "Link"], + ["2", "SymbolicLink"], + // Devices and FIFOs aren't fully supported + // they are parsed, but skipped when unpacking + ["3", "CharacterDevice"], + ["4", "BlockDevice"], + ["5", "Directory"], + ["6", "FIFO"], + // same as File + ["7", "ContiguousFile"], + // pax headers + ["g", "GlobalExtendedHeader"], + ["x", "ExtendedHeader"], + // vendor-specific stuff + // skip + ["A", "SolarisACL"], + // like 5, but with data, which should be skipped + ["D", "GNUDumpDir"], + // metadata only, skip + ["I", "Inode"], + // data = link path of next file + ["K", "NextFileHasLongLinkpath"], + // data = path of next file + ["L", "NextFileHasLongPath"], + // skip + ["M", "ContinuationFile"], + // like L + ["N", "OldGnuLongPath"], + // skip + ["S", "SparseFile"], + // skip + ["V", "TapeVolumeHeader"], + // like x + ["X", "OldExtendedHeader"] + ]); + code = new Map(Array.from(name).map((kv) => [kv[1], kv[0]])); + } +}); + +// .yarn/cache/tar-npm-7.5.1-7b414f7fec-0dad0596a6.zip/node_modules/tar/dist/esm/header.js +var import_node_path, Header, splitPrefix, decString, decDate, numToDate, decNumber, nanUndef, decSmallNumber, MAXNUM, encNumber, encSmallNumber, octalString, padOctal, encDate, NULLS, encString; +var init_header = __esm({ + ".yarn/cache/tar-npm-7.5.1-7b414f7fec-0dad0596a6.zip/node_modules/tar/dist/esm/header.js"() { + import_node_path = require("node:path"); + init_large_numbers(); + init_types(); + Header = class { + cksumValid = false; + needPax = false; + nullBlock = false; + block; + path; + mode; + uid; + gid; + size; + cksum; + #type = "Unsupported"; + linkpath; + uname; + gname; + devmaj = 0; + devmin = 0; + atime; + ctime; + mtime; + charset; + comment; + constructor(data, off = 0, ex, gex) { + if (Buffer.isBuffer(data)) { + this.decode(data, off || 0, ex, gex); + } else if (data) { + this.#slurp(data); } - return this.length; } - unshift(...args) { - for (var i = 0, l = args.length; i < l; i++) { - unshift(this, args[i]); + decode(buf, off, ex, gex) { + if (!off) { + off = 0; } - return this.length; - } - pop() { - if (!this.tail) { - return void 0; + if (!buf || !(buf.length >= off + 512)) { + throw new Error("need 512 bytes for header"); } - const res = this.tail.value; - const t = this.tail; - this.tail = this.tail.prev; - if (this.tail) { - this.tail.next = void 0; - } else { - this.head = void 0; + this.path = decString(buf, off, 100); + this.mode = decNumber(buf, off + 100, 8); + this.uid = decNumber(buf, off + 108, 8); + this.gid = decNumber(buf, off + 116, 8); + this.size = decNumber(buf, off + 124, 12); + this.mtime = decDate(buf, off + 136, 12); + this.cksum = decNumber(buf, off + 148, 12); + if (gex) + this.#slurp(gex, true); + if (ex) + this.#slurp(ex); + const t = decString(buf, off + 156, 1); + if (isCode(t)) { + this.#type = t || "0"; } - t.list = void 0; - this.length--; - return res; - } - shift() { - if (!this.head) { - return void 0; + if (this.#type === "0" && this.path.slice(-1) === "/") { + this.#type = "5"; } - const res = this.head.value; - const h = this.head; - this.head = this.head.next; - if (this.head) { - this.head.prev = void 0; - } else { - this.tail = void 0; + if (this.#type === "5") { + this.size = 0; } - h.list = void 0; - this.length--; - return res; - } - forEach(fn2, thisp) { - thisp = thisp || this; - for (let walker = this.head, i = 0; !!walker; i++) { - fn2.call(thisp, walker.value, i, this); - walker = walker.next; + this.linkpath = decString(buf, off + 157, 100); + if (buf.subarray(off + 257, off + 265).toString() === "ustar\x0000") { + this.uname = decString(buf, off + 265, 32); + this.gname = decString(buf, off + 297, 32); + this.devmaj = decNumber(buf, off + 329, 8) ?? 0; + this.devmin = decNumber(buf, off + 337, 8) ?? 0; + if (buf[off + 475] !== 0) { + const prefix = decString(buf, off + 345, 155); + this.path = prefix + "/" + this.path; + } else { + const prefix = decString(buf, off + 345, 130); + if (prefix) { + this.path = prefix + "/" + this.path; + } + this.atime = decDate(buf, off + 476, 12); + this.ctime = decDate(buf, off + 488, 12); + } } - } - forEachReverse(fn2, thisp) { - thisp = thisp || this; - for (let walker = this.tail, i = this.length - 1; !!walker; i--) { - fn2.call(thisp, walker.value, i, this); - walker = walker.prev; + let sum = 8 * 32; + for (let i = off; i < off + 148; i++) { + sum += buf[i]; } - } - get(n) { - let i = 0; - let walker = this.head; - for (; !!walker && i < n; i++) { - walker = walker.next; + for (let i = off + 156; i < off + 512; i++) { + sum += buf[i]; } - if (i === n && !!walker) { - return walker.value; + this.cksumValid = sum === this.cksum; + if (this.cksum === void 0 && sum === 8 * 32) { + this.nullBlock = true; } } - getReverse(n) { - let i = 0; - let walker = this.tail; - for (; !!walker && i < n; i++) { - walker = walker.prev; - } - if (i === n && !!walker) { - return walker.value; - } + #slurp(ex, gex = false) { + Object.assign(this, Object.fromEntries(Object.entries(ex).filter(([k, v]) => { + return !(v === null || v === void 0 || k === "path" && gex || k === "linkpath" && gex || k === "global"); + }))); } - map(fn2, thisp) { - thisp = thisp || this; - const res = new _Yallist(); - for (let walker = this.head; !!walker; ) { - res.push(fn2.call(thisp, walker.value, this)); - walker = walker.next; + encode(buf, off = 0) { + if (!buf) { + buf = this.block = Buffer.alloc(512); } - return res; - } - mapReverse(fn2, thisp) { - thisp = thisp || this; - var res = new _Yallist(); - for (let walker = this.tail; !!walker; ) { - res.push(fn2.call(thisp, walker.value, this)); - walker = walker.prev; + if (this.#type === "Unsupported") { + this.#type = "0"; } - return res; - } - reduce(fn2, initial) { - let acc; - let walker = this.head; - if (arguments.length > 1) { - acc = initial; - } else if (this.head) { - walker = this.head.next; - acc = this.head.value; - } else { - throw new TypeError("Reduce of empty list with no initial value"); + if (!(buf.length >= off + 512)) { + throw new Error("need 512 bytes for header"); } - for (var i = 0; !!walker; i++) { - acc = fn2(acc, walker.value, i); - walker = walker.next; - } - return acc; - } - reduceReverse(fn2, initial) { - let acc; - let walker = this.tail; - if (arguments.length > 1) { - acc = initial; - } else if (this.tail) { - walker = this.tail.prev; - acc = this.tail.value; + const prefixSize = this.ctime || this.atime ? 130 : 155; + const split = splitPrefix(this.path || "", prefixSize); + const path16 = split[0]; + const prefix = split[1]; + this.needPax = !!split[2]; + this.needPax = encString(buf, off, 100, path16) || this.needPax; + this.needPax = encNumber(buf, off + 100, 8, this.mode) || this.needPax; + this.needPax = encNumber(buf, off + 108, 8, this.uid) || this.needPax; + this.needPax = encNumber(buf, off + 116, 8, this.gid) || this.needPax; + this.needPax = encNumber(buf, off + 124, 12, this.size) || this.needPax; + this.needPax = encDate(buf, off + 136, 12, this.mtime) || this.needPax; + buf[off + 156] = this.#type.charCodeAt(0); + this.needPax = encString(buf, off + 157, 100, this.linkpath) || this.needPax; + buf.write("ustar\x0000", off + 257, 8); + this.needPax = encString(buf, off + 265, 32, this.uname) || this.needPax; + this.needPax = encString(buf, off + 297, 32, this.gname) || this.needPax; + this.needPax = encNumber(buf, off + 329, 8, this.devmaj) || this.needPax; + this.needPax = encNumber(buf, off + 337, 8, this.devmin) || this.needPax; + this.needPax = encString(buf, off + 345, prefixSize, prefix) || this.needPax; + if (buf[off + 475] !== 0) { + this.needPax = encString(buf, off + 345, 155, prefix) || this.needPax; } else { - throw new TypeError("Reduce of empty list with no initial value"); - } - for (let i = this.length - 1; !!walker; i--) { - acc = fn2(acc, walker.value, i); - walker = walker.prev; - } - return acc; - } - toArray() { - const arr = new Array(this.length); - for (let i = 0, walker = this.head; !!walker; i++) { - arr[i] = walker.value; - walker = walker.next; - } - return arr; - } - toArrayReverse() { - const arr = new Array(this.length); - for (let i = 0, walker = this.tail; !!walker; i++) { - arr[i] = walker.value; - walker = walker.prev; - } - return arr; - } - slice(from = 0, to = this.length) { - if (to < 0) { - to += this.length; - } - if (from < 0) { - from += this.length; - } - const ret = new _Yallist(); - if (to < from || to < 0) { - return ret; - } - if (from < 0) { - from = 0; - } - if (to > this.length) { - to = this.length; + this.needPax = encString(buf, off + 345, 130, prefix) || this.needPax; + this.needPax = encDate(buf, off + 476, 12, this.atime) || this.needPax; + this.needPax = encDate(buf, off + 488, 12, this.ctime) || this.needPax; } - let walker = this.head; - let i = 0; - for (i = 0; !!walker && i < from; i++) { - walker = walker.next; + let sum = 8 * 32; + for (let i = off; i < off + 148; i++) { + sum += buf[i]; } - for (; !!walker && i < to; i++, walker = walker.next) { - ret.push(walker.value); + for (let i = off + 156; i < off + 512; i++) { + sum += buf[i]; } - return ret; + this.cksum = sum; + encNumber(buf, off + 148, 8, this.cksum); + this.cksumValid = true; + return this.needPax; } - sliceReverse(from = 0, to = this.length) { - if (to < 0) { - to += this.length; - } - if (from < 0) { - from += this.length; - } - const ret = new _Yallist(); - if (to < from || to < 0) { - return ret; - } - if (from < 0) { - from = 0; - } - if (to > this.length) { - to = this.length; - } - let i = this.length; - let walker = this.tail; - for (; !!walker && i > to; i--) { - walker = walker.prev; - } - for (; !!walker && i > from; i--, walker = walker.prev) { - ret.push(walker.value); - } - return ret; + get type() { + return this.#type === "Unsupported" ? this.#type : name.get(this.#type); } - splice(start, deleteCount = 0, ...nodes) { - if (start > this.length) { - start = this.length - 1; - } - if (start < 0) { - start = this.length + start; - } - let walker = this.head; - for (let i = 0; !!walker && i < start; i++) { - walker = walker.next; - } - const ret = []; - for (let i = 0; !!walker && i < deleteCount; i++) { - ret.push(walker.value); - walker = this.removeNode(walker); - } - if (!walker) { - walker = this.tail; - } else if (walker !== this.tail) { - walker = walker.prev; - } - for (const v of nodes) { - walker = insertAfter(this, walker, v); - } - return ret; + get typeKey() { + return this.#type; } - reverse() { - const head = this.head; - const tail = this.tail; - for (let walker = head; !!walker; walker = walker.prev) { - const p = walker.prev; - walker.prev = walker.next; - walker.next = p; + set type(type) { + const c = String(code.get(type)); + if (isCode(c) || c === "Unsupported") { + this.#type = c; + } else if (isCode(type)) { + this.#type = type; + } else { + throw new TypeError("invalid entry type: " + type); } - this.head = tail; - this.tail = head; - return this; } }; - Node = class { - list; - next; - prev; - value; - constructor(value, prev, next, list2) { - this.list = list2; - this.value = value; - if (prev) { - prev.next = this; - this.prev = prev; - } else { - this.prev = void 0; - } - if (next) { - next.prev = this; - this.next = next; - } else { - this.next = void 0; + splitPrefix = (p, prefixSize) => { + const pathSize = 100; + let pp = p; + let prefix = ""; + let ret = void 0; + const root = import_node_path.posix.parse(p).root || "."; + if (Buffer.byteLength(pp) < pathSize) { + ret = [pp, prefix, false]; + } else { + prefix = import_node_path.posix.dirname(pp); + pp = import_node_path.posix.basename(pp); + do { + if (Buffer.byteLength(pp) <= pathSize && Buffer.byteLength(prefix) <= prefixSize) { + ret = [pp, prefix, false]; + } else if (Buffer.byteLength(pp) > pathSize && Buffer.byteLength(prefix) <= prefixSize) { + ret = [pp.slice(0, pathSize - 1), prefix, true]; + } else { + pp = import_node_path.posix.join(import_node_path.posix.basename(prefix), pp); + prefix = import_node_path.posix.dirname(prefix); + } + } while (prefix !== root && ret === void 0); + if (!ret) { + ret = [p.slice(0, pathSize - 1), "", true]; } } + return ret; + }; + decString = (buf, off, size) => buf.subarray(off, off + size).toString("utf8").replace(/\0.*/, ""); + decDate = (buf, off, size) => numToDate(decNumber(buf, off, size)); + numToDate = (num) => num === void 0 ? void 0 : new Date(num * 1e3); + decNumber = (buf, off, size) => Number(buf[off]) & 128 ? parse(buf.subarray(off, off + size)) : decSmallNumber(buf, off, size); + nanUndef = (value) => isNaN(value) ? void 0 : value; + decSmallNumber = (buf, off, size) => nanUndef(parseInt(buf.subarray(off, off + size).toString("utf8").replace(/\0.*$/, "").trim(), 8)); + MAXNUM = { + 12: 8589934591, + 8: 2097151 }; + encNumber = (buf, off, size, num) => num === void 0 ? false : num > MAXNUM[size] || num < 0 ? (encode(num, buf.subarray(off, off + size)), true) : (encSmallNumber(buf, off, size, num), false); + encSmallNumber = (buf, off, size, num) => buf.write(octalString(num, size), off, size, "ascii"); + octalString = (num, size) => padOctal(Math.floor(num).toString(8), size); + padOctal = (str, size) => (str.length === size - 1 ? str : new Array(size - str.length - 1).join("0") + str + " ") + "\0"; + encDate = (buf, off, size, date) => date === void 0 ? false : encNumber(buf, off, size, date.getTime() / 1e3); + NULLS = new Array(156).join("\0"); + encString = (buf, off, size, str) => str === void 0 ? false : (buf.write(str + NULLS, off, size, "utf8"), str.length !== Buffer.byteLength(str) || str.length > size); } }); -// .yarn/cache/tar-npm-7.4.3-1dbbd1ffc3-d4679609bb.zip/node_modules/tar/dist/esm/large-numbers.js -var encode, encodePositive, encodeNegative, parse, twos, pos, onesComp, twosComp; -var init_large_numbers = __esm({ - ".yarn/cache/tar-npm-7.4.3-1dbbd1ffc3-d4679609bb.zip/node_modules/tar/dist/esm/large-numbers.js"() { - encode = (num, buf) => { - if (!Number.isSafeInteger(num)) { - throw Error("cannot encode number outside of javascript safe integer range"); - } else if (num < 0) { - encodeNegative(num, buf); - } else { - encodePositive(num, buf); - } - return buf; - }; - encodePositive = (num, buf) => { - buf[0] = 128; - for (var i = buf.length; i > 1; i--) { - buf[i - 1] = num & 255; - num = Math.floor(num / 256); - } - }; - encodeNegative = (num, buf) => { - buf[0] = 255; - var flipped = false; - num = num * -1; - for (var i = buf.length; i > 1; i--) { - var byte = num & 255; - num = Math.floor(num / 256); - if (flipped) { - buf[i - 1] = onesComp(byte); - } else if (byte === 0) { - buf[i - 1] = 0; - } else { - flipped = true; - buf[i - 1] = twosComp(byte); - } +// .yarn/cache/tar-npm-7.5.1-7b414f7fec-0dad0596a6.zip/node_modules/tar/dist/esm/pax.js +var import_node_path2, Pax, merge, parseKV, parseKVLine; +var init_pax = __esm({ + ".yarn/cache/tar-npm-7.5.1-7b414f7fec-0dad0596a6.zip/node_modules/tar/dist/esm/pax.js"() { + import_node_path2 = require("node:path"); + init_header(); + Pax = class _Pax { + atime; + mtime; + ctime; + charset; + comment; + gid; + uid; + gname; + uname; + linkpath; + dev; + ino; + nlink; + path; + size; + mode; + global; + constructor(obj, global2 = false) { + this.atime = obj.atime; + this.charset = obj.charset; + this.comment = obj.comment; + this.ctime = obj.ctime; + this.dev = obj.dev; + this.gid = obj.gid; + this.global = global2; + this.gname = obj.gname; + this.ino = obj.ino; + this.linkpath = obj.linkpath; + this.mtime = obj.mtime; + this.nlink = obj.nlink; + this.path = obj.path; + this.size = obj.size; + this.uid = obj.uid; + this.uname = obj.uname; } - }; - parse = (buf) => { - const pre = buf[0]; - const value = pre === 128 ? pos(buf.subarray(1, buf.length)) : pre === 255 ? twos(buf) : null; - if (value === null) { - throw Error("invalid base256 encoding"); + encode() { + const body = this.encodeBody(); + if (body === "") { + return Buffer.allocUnsafe(0); + } + const bodyLen = Buffer.byteLength(body); + const bufLen = 512 * Math.ceil(1 + bodyLen / 512); + const buf = Buffer.allocUnsafe(bufLen); + for (let i = 0; i < 512; i++) { + buf[i] = 0; + } + new Header({ + // XXX split the path + // then the path should be PaxHeader + basename, but less than 99, + // prepend with the dirname + /* c8 ignore start */ + path: ("PaxHeader/" + (0, import_node_path2.basename)(this.path ?? "")).slice(0, 99), + /* c8 ignore stop */ + mode: this.mode || 420, + uid: this.uid, + gid: this.gid, + size: bodyLen, + mtime: this.mtime, + type: this.global ? "GlobalExtendedHeader" : "ExtendedHeader", + linkpath: "", + uname: this.uname || "", + gname: this.gname || "", + devmaj: 0, + devmin: 0, + atime: this.atime, + ctime: this.ctime + }).encode(buf); + buf.write(body, 512, bodyLen, "utf8"); + for (let i = bodyLen + 512; i < buf.length; i++) { + buf[i] = 0; + } + return buf; } - if (!Number.isSafeInteger(value)) { - throw Error("parsed number outside of javascript safe integer range"); + encodeBody() { + return this.encodeField("path") + this.encodeField("ctime") + this.encodeField("atime") + this.encodeField("dev") + this.encodeField("ino") + this.encodeField("nlink") + this.encodeField("charset") + this.encodeField("comment") + this.encodeField("gid") + this.encodeField("gname") + this.encodeField("linkpath") + this.encodeField("mtime") + this.encodeField("size") + this.encodeField("uid") + this.encodeField("uname"); } - return value; - }; - twos = (buf) => { - var len = buf.length; - var sum = 0; - var flipped = false; - for (var i = len - 1; i > -1; i--) { - var byte = Number(buf[i]); - var f; - if (flipped) { - f = onesComp(byte); - } else if (byte === 0) { - f = byte; - } else { - flipped = true; - f = twosComp(byte); + encodeField(field) { + if (this[field] === void 0) { + return ""; } - if (f !== 0) { - sum -= f * Math.pow(256, len - i - 1); + const r = this[field]; + const v = r instanceof Date ? r.getTime() / 1e3 : r; + const s = " " + (field === "dev" || field === "ino" || field === "nlink" ? "SCHILY." : "") + field + "=" + v + "\n"; + const byteLen = Buffer.byteLength(s); + let digits = Math.floor(Math.log(byteLen) / Math.log(10)) + 1; + if (byteLen + digits >= Math.pow(10, digits)) { + digits += 1; } + const len = digits + byteLen; + return len + s; + } + static parse(str, ex, g = false) { + return new _Pax(merge(parseKV(str), ex), g); } - return sum; }; - pos = (buf) => { - var len = buf.length; - var sum = 0; - for (var i = len - 1; i > -1; i--) { - var byte = Number(buf[i]); - if (byte !== 0) { - sum += byte * Math.pow(256, len - i - 1); - } + merge = (a, b) => b ? Object.assign({}, b, a) : a; + parseKV = (str) => str.replace(/\n$/, "").split("\n").reduce(parseKVLine, /* @__PURE__ */ Object.create(null)); + parseKVLine = (set, line) => { + const n = parseInt(line, 10); + if (n !== Buffer.byteLength(line) + 1) { + return set; } - return sum; + line = line.slice((n + " ").length); + const kv = line.split("="); + const r = kv.shift(); + if (!r) { + return set; + } + const k = r.replace(/^SCHILY\.(dev|ino|nlink)/, "$1"); + const v = kv.join("="); + set[k] = /^([A-Z]+\.)?([mac]|birth|creation)time$/.test(k) ? new Date(Number(v) * 1e3) : /^[0-9]+$/.test(v) ? +v : v; + return set; }; - onesComp = (byte) => (255 ^ byte) & 255; - twosComp = (byte) => (255 ^ byte) + 1 & 255; } }); -// .yarn/cache/tar-npm-7.4.3-1dbbd1ffc3-d4679609bb.zip/node_modules/tar/dist/esm/types.js -var isCode, name, code; -var init_types = __esm({ - ".yarn/cache/tar-npm-7.4.3-1dbbd1ffc3-d4679609bb.zip/node_modules/tar/dist/esm/types.js"() { - isCode = (c) => name.has(c); - name = /* @__PURE__ */ new Map([ - ["0", "File"], - // same as File - ["", "OldFile"], - ["1", "Link"], - ["2", "SymbolicLink"], - // Devices and FIFOs aren't fully supported - // they are parsed, but skipped when unpacking - ["3", "CharacterDevice"], - ["4", "BlockDevice"], - ["5", "Directory"], - ["6", "FIFO"], - // same as File - ["7", "ContiguousFile"], - // pax headers - ["g", "GlobalExtendedHeader"], - ["x", "ExtendedHeader"], - // vendor-specific stuff - // skip - ["A", "SolarisACL"], - // like 5, but with data, which should be skipped - ["D", "GNUDumpDir"], - // metadata only, skip - ["I", "Inode"], - // data = link path of next file - ["K", "NextFileHasLongLinkpath"], - // data = path of next file - ["L", "NextFileHasLongPath"], - // skip - ["M", "ContinuationFile"], - // like L - ["N", "OldGnuLongPath"], - // skip - ["S", "SparseFile"], - // skip - ["V", "TapeVolumeHeader"], - // like x - ["X", "OldExtendedHeader"] - ]); - code = new Map(Array.from(name).map((kv) => [kv[1], kv[0]])); +// .yarn/cache/tar-npm-7.5.1-7b414f7fec-0dad0596a6.zip/node_modules/tar/dist/esm/normalize-windows-path.js +var platform, normalizeWindowsPath; +var init_normalize_windows_path = __esm({ + ".yarn/cache/tar-npm-7.5.1-7b414f7fec-0dad0596a6.zip/node_modules/tar/dist/esm/normalize-windows-path.js"() { + platform = process.env.TESTING_TAR_FAKE_PLATFORM || process.platform; + normalizeWindowsPath = platform !== "win32" ? (p) => p : (p) => p && p.replace(/\\/g, "/"); } }); -// .yarn/cache/tar-npm-7.4.3-1dbbd1ffc3-d4679609bb.zip/node_modules/tar/dist/esm/header.js -var import_node_path, Header, splitPrefix, decString, decDate, numToDate, decNumber, nanUndef, decSmallNumber, MAXNUM, encNumber, encSmallNumber, octalString, padOctal, encDate, NULLS, encString; -var init_header = __esm({ - ".yarn/cache/tar-npm-7.4.3-1dbbd1ffc3-d4679609bb.zip/node_modules/tar/dist/esm/header.js"() { - import_node_path = require("node:path"); - init_large_numbers(); - init_types(); - Header = class { - cksumValid = false; - needPax = false; - nullBlock = false; - block; +// .yarn/cache/tar-npm-7.5.1-7b414f7fec-0dad0596a6.zip/node_modules/tar/dist/esm/read-entry.js +var ReadEntry; +var init_read_entry = __esm({ + ".yarn/cache/tar-npm-7.5.1-7b414f7fec-0dad0596a6.zip/node_modules/tar/dist/esm/read-entry.js"() { + init_esm(); + init_normalize_windows_path(); + ReadEntry = class extends Minipass { + extended; + globalExtended; + header; + startBlockSize; + blockRemain; + remain; + type; + meta = false; + ignore = false; path; mode; uid; gid; - size; - cksum; - #type = "Unsupported"; - linkpath; uname; gname; - devmaj = 0; - devmin = 0; + size = 0; + mtime; atime; ctime; - mtime; - charset; - comment; - constructor(data, off = 0, ex, gex) { - if (Buffer.isBuffer(data)) { - this.decode(data, off || 0, ex, gex); - } else if (data) { - this.#slurp(data); - } - } - decode(buf, off, ex, gex) { - if (!off) { - off = 0; - } - if (!buf || !(buf.length >= off + 512)) { - throw new Error("need 512 bytes for header"); - } - this.path = decString(buf, off, 100); - this.mode = decNumber(buf, off + 100, 8); - this.uid = decNumber(buf, off + 108, 8); - this.gid = decNumber(buf, off + 116, 8); - this.size = decNumber(buf, off + 124, 12); - this.mtime = decDate(buf, off + 136, 12); - this.cksum = decNumber(buf, off + 148, 12); - if (gex) - this.#slurp(gex, true); - if (ex) - this.#slurp(ex); - const t = decString(buf, off + 156, 1); - if (isCode(t)) { - this.#type = t || "0"; + linkpath; + dev; + ino; + nlink; + invalid = false; + absolute; + unsupported = false; + constructor(header, ex, gex) { + super({}); + this.pause(); + this.extended = ex; + this.globalExtended = gex; + this.header = header; + this.remain = header.size ?? 0; + this.startBlockSize = 512 * Math.ceil(this.remain / 512); + this.blockRemain = this.startBlockSize; + this.type = header.type; + switch (this.type) { + case "File": + case "OldFile": + case "Link": + case "SymbolicLink": + case "CharacterDevice": + case "BlockDevice": + case "Directory": + case "FIFO": + case "ContiguousFile": + case "GNUDumpDir": + break; + case "NextFileHasLongLinkpath": + case "NextFileHasLongPath": + case "OldGnuLongPath": + case "GlobalExtendedHeader": + case "ExtendedHeader": + case "OldExtendedHeader": + this.meta = true; + break; + // NOTE: gnutar and bsdtar treat unrecognized types as 'File' + // it may be worth doing the same, but with a warning. + default: + this.ignore = true; } - if (this.#type === "0" && this.path.slice(-1) === "/") { - this.#type = "5"; + if (!header.path) { + throw new Error("no path provided for tar.ReadEntry"); } - if (this.#type === "5") { - this.size = 0; + this.path = normalizeWindowsPath(header.path); + this.mode = header.mode; + if (this.mode) { + this.mode = this.mode & 4095; } - this.linkpath = decString(buf, off + 157, 100); - if (buf.subarray(off + 257, off + 265).toString() === "ustar\x0000") { - this.uname = decString(buf, off + 265, 32); - this.gname = decString(buf, off + 297, 32); - this.devmaj = decNumber(buf, off + 329, 8) ?? 0; - this.devmin = decNumber(buf, off + 337, 8) ?? 0; - if (buf[off + 475] !== 0) { - const prefix = decString(buf, off + 345, 155); - this.path = prefix + "/" + this.path; - } else { - const prefix = decString(buf, off + 345, 130); - if (prefix) { - this.path = prefix + "/" + this.path; - } - this.atime = decDate(buf, off + 476, 12); - this.ctime = decDate(buf, off + 488, 12); - } + this.uid = header.uid; + this.gid = header.gid; + this.uname = header.uname; + this.gname = header.gname; + this.size = this.remain; + this.mtime = header.mtime; + this.atime = header.atime; + this.ctime = header.ctime; + this.linkpath = header.linkpath ? normalizeWindowsPath(header.linkpath) : void 0; + this.uname = header.uname; + this.gname = header.gname; + if (ex) { + this.#slurp(ex); } - let sum = 8 * 32; - for (let i = off; i < off + 148; i++) { - sum += buf[i]; + if (gex) { + this.#slurp(gex, true); } - for (let i = off + 156; i < off + 512; i++) { - sum += buf[i]; + } + write(data) { + const writeLen = data.length; + if (writeLen > this.blockRemain) { + throw new Error("writing more to entry than is appropriate"); } - this.cksumValid = sum === this.cksum; - if (this.cksum === void 0 && sum === 8 * 32) { - this.nullBlock = true; + const r = this.remain; + const br = this.blockRemain; + this.remain = Math.max(0, r - writeLen); + this.blockRemain = Math.max(0, br - writeLen); + if (this.ignore) { + return true; } + if (r >= writeLen) { + return super.write(data); + } + return super.write(data.subarray(0, r)); } #slurp(ex, gex = false) { + if (ex.path) + ex.path = normalizeWindowsPath(ex.path); + if (ex.linkpath) + ex.linkpath = normalizeWindowsPath(ex.linkpath); Object.assign(this, Object.fromEntries(Object.entries(ex).filter(([k, v]) => { - return !(v === null || v === void 0 || k === "path" && gex || k === "linkpath" && gex || k === "global"); + return !(v === null || v === void 0 || k === "path" && gex); }))); } - encode(buf, off = 0) { - if (!buf) { - buf = this.block = Buffer.alloc(512); - } - if (this.#type === "Unsupported") { - this.#type = "0"; - } - if (!(buf.length >= off + 512)) { - throw new Error("need 512 bytes for header"); - } - const prefixSize = this.ctime || this.atime ? 130 : 155; - const split = splitPrefix(this.path || "", prefixSize); - const path16 = split[0]; - const prefix = split[1]; - this.needPax = !!split[2]; - this.needPax = encString(buf, off, 100, path16) || this.needPax; - this.needPax = encNumber(buf, off + 100, 8, this.mode) || this.needPax; - this.needPax = encNumber(buf, off + 108, 8, this.uid) || this.needPax; - this.needPax = encNumber(buf, off + 116, 8, this.gid) || this.needPax; - this.needPax = encNumber(buf, off + 124, 12, this.size) || this.needPax; - this.needPax = encDate(buf, off + 136, 12, this.mtime) || this.needPax; - buf[off + 156] = this.#type.charCodeAt(0); - this.needPax = encString(buf, off + 157, 100, this.linkpath) || this.needPax; - buf.write("ustar\x0000", off + 257, 8); - this.needPax = encString(buf, off + 265, 32, this.uname) || this.needPax; - this.needPax = encString(buf, off + 297, 32, this.gname) || this.needPax; - this.needPax = encNumber(buf, off + 329, 8, this.devmaj) || this.needPax; - this.needPax = encNumber(buf, off + 337, 8, this.devmin) || this.needPax; - this.needPax = encString(buf, off + 345, prefixSize, prefix) || this.needPax; - if (buf[off + 475] !== 0) { - this.needPax = encString(buf, off + 345, 155, prefix) || this.needPax; - } else { - this.needPax = encString(buf, off + 345, 130, prefix) || this.needPax; - this.needPax = encDate(buf, off + 476, 12, this.atime) || this.needPax; - this.needPax = encDate(buf, off + 488, 12, this.ctime) || this.needPax; - } - let sum = 8 * 32; - for (let i = off; i < off + 148; i++) { - sum += buf[i]; - } - for (let i = off + 156; i < off + 512; i++) { - sum += buf[i]; - } - this.cksum = sum; - encNumber(buf, off + 148, 8, this.cksum); - this.cksumValid = true; - return this.needPax; - } - get type() { - return this.#type === "Unsupported" ? this.#type : name.get(this.#type); + }; + } +}); + +// .yarn/cache/tar-npm-7.5.1-7b414f7fec-0dad0596a6.zip/node_modules/tar/dist/esm/warn-method.js +var warnMethod; +var init_warn_method = __esm({ + ".yarn/cache/tar-npm-7.5.1-7b414f7fec-0dad0596a6.zip/node_modules/tar/dist/esm/warn-method.js"() { + warnMethod = (self2, code2, message, data = {}) => { + if (self2.file) { + data.file = self2.file; } - get typeKey() { - return this.#type; + if (self2.cwd) { + data.cwd = self2.cwd; } - set type(type) { - const c = String(code.get(type)); - if (isCode(c) || c === "Unsupported") { - this.#type = c; - } else if (isCode(type)) { - this.#type = type; - } else { - throw new TypeError("invalid entry type: " + type); + data.code = message instanceof Error && message.code || code2; + data.tarCode = code2; + if (!self2.strict && data.recoverable !== false) { + if (message instanceof Error) { + data = Object.assign(message, data); + message = message.message; } - } - }; - splitPrefix = (p, prefixSize) => { - const pathSize = 100; - let pp = p; - let prefix = ""; - let ret = void 0; - const root = import_node_path.posix.parse(p).root || "."; - if (Buffer.byteLength(pp) < pathSize) { - ret = [pp, prefix, false]; + self2.emit("warn", code2, message, data); + } else if (message instanceof Error) { + self2.emit("error", Object.assign(message, data)); } else { - prefix = import_node_path.posix.dirname(pp); - pp = import_node_path.posix.basename(pp); - do { - if (Buffer.byteLength(pp) <= pathSize && Buffer.byteLength(prefix) <= prefixSize) { - ret = [pp, prefix, false]; - } else if (Buffer.byteLength(pp) > pathSize && Buffer.byteLength(prefix) <= prefixSize) { - ret = [pp.slice(0, pathSize - 1), prefix, true]; - } else { - pp = import_node_path.posix.join(import_node_path.posix.basename(prefix), pp); - prefix = import_node_path.posix.dirname(prefix); - } - } while (prefix !== root && ret === void 0); - if (!ret) { - ret = [p.slice(0, pathSize - 1), "", true]; - } + self2.emit("error", Object.assign(new Error(`${code2}: ${message}`), data)); } - return ret; - }; - decString = (buf, off, size) => buf.subarray(off, off + size).toString("utf8").replace(/\0.*/, ""); - decDate = (buf, off, size) => numToDate(decNumber(buf, off, size)); - numToDate = (num) => num === void 0 ? void 0 : new Date(num * 1e3); - decNumber = (buf, off, size) => Number(buf[off]) & 128 ? parse(buf.subarray(off, off + size)) : decSmallNumber(buf, off, size); - nanUndef = (value) => isNaN(value) ? void 0 : value; - decSmallNumber = (buf, off, size) => nanUndef(parseInt(buf.subarray(off, off + size).toString("utf8").replace(/\0.*$/, "").trim(), 8)); - MAXNUM = { - 12: 8589934591, - 8: 2097151 }; - encNumber = (buf, off, size, num) => num === void 0 ? false : num > MAXNUM[size] || num < 0 ? (encode(num, buf.subarray(off, off + size)), true) : (encSmallNumber(buf, off, size, num), false); - encSmallNumber = (buf, off, size, num) => buf.write(octalString(num, size), off, size, "ascii"); - octalString = (num, size) => padOctal(Math.floor(num).toString(8), size); - padOctal = (str, size) => (str.length === size - 1 ? str : new Array(size - str.length - 1).join("0") + str + " ") + "\0"; - encDate = (buf, off, size, date) => date === void 0 ? false : encNumber(buf, off, size, date.getTime() / 1e3); - NULLS = new Array(156).join("\0"); - encString = (buf, off, size, str) => str === void 0 ? false : (buf.write(str + NULLS, off, size, "utf8"), str.length !== Buffer.byteLength(str) || str.length > size); } }); -// .yarn/cache/tar-npm-7.4.3-1dbbd1ffc3-d4679609bb.zip/node_modules/tar/dist/esm/pax.js -var import_node_path2, Pax, merge, parseKV, parseKVLine; -var init_pax = __esm({ - ".yarn/cache/tar-npm-7.4.3-1dbbd1ffc3-d4679609bb.zip/node_modules/tar/dist/esm/pax.js"() { - import_node_path2 = require("node:path"); +// .yarn/cache/tar-npm-7.5.1-7b414f7fec-0dad0596a6.zip/node_modules/tar/dist/esm/parse.js +var import_events3, maxMetaEntrySize, gzipHeader, zstdHeader, ZIP_HEADER_LEN, STATE, WRITEENTRY, READENTRY, NEXTENTRY, PROCESSENTRY, EX, GEX, META, EMITMETA, BUFFER2, QUEUE, ENDED, EMITTEDEND, EMIT, UNZIP, CONSUMECHUNK, CONSUMECHUNKSUB, CONSUMEBODY, CONSUMEMETA, CONSUMEHEADER, CONSUMING, BUFFERCONCAT, MAYBEEND, WRITING, ABORTED2, DONE, SAW_VALID_ENTRY, SAW_NULL_BLOCK, SAW_EOF, CLOSESTREAM, noop2, Parser; +var init_parse = __esm({ + ".yarn/cache/tar-npm-7.5.1-7b414f7fec-0dad0596a6.zip/node_modules/tar/dist/esm/parse.js"() { + import_events3 = require("events"); + init_esm3(); init_header(); - Pax = class _Pax { - atime; - mtime; - ctime; - charset; - comment; - gid; - uid; - gname; - uname; - linkpath; - dev; - ino; - nlink; - path; - size; - mode; - global; - constructor(obj, global2 = false) { - this.atime = obj.atime; - this.charset = obj.charset; - this.comment = obj.comment; - this.ctime = obj.ctime; - this.dev = obj.dev; - this.gid = obj.gid; - this.global = global2; - this.gname = obj.gname; - this.ino = obj.ino; - this.linkpath = obj.linkpath; - this.mtime = obj.mtime; - this.nlink = obj.nlink; - this.path = obj.path; - this.size = obj.size; - this.uid = obj.uid; - this.uname = obj.uname; - } - encode() { - const body = this.encodeBody(); - if (body === "") { - return Buffer.allocUnsafe(0); + init_pax(); + init_read_entry(); + init_warn_method(); + maxMetaEntrySize = 1024 * 1024; + gzipHeader = Buffer.from([31, 139]); + zstdHeader = Buffer.from([40, 181, 47, 253]); + ZIP_HEADER_LEN = Math.max(gzipHeader.length, zstdHeader.length); + STATE = Symbol("state"); + WRITEENTRY = Symbol("writeEntry"); + READENTRY = Symbol("readEntry"); + NEXTENTRY = Symbol("nextEntry"); + PROCESSENTRY = Symbol("processEntry"); + EX = Symbol("extendedHeader"); + GEX = Symbol("globalExtendedHeader"); + META = Symbol("meta"); + EMITMETA = Symbol("emitMeta"); + BUFFER2 = Symbol("buffer"); + QUEUE = Symbol("queue"); + ENDED = Symbol("ended"); + EMITTEDEND = Symbol("emittedEnd"); + EMIT = Symbol("emit"); + UNZIP = Symbol("unzip"); + CONSUMECHUNK = Symbol("consumeChunk"); + CONSUMECHUNKSUB = Symbol("consumeChunkSub"); + CONSUMEBODY = Symbol("consumeBody"); + CONSUMEMETA = Symbol("consumeMeta"); + CONSUMEHEADER = Symbol("consumeHeader"); + CONSUMING = Symbol("consuming"); + BUFFERCONCAT = Symbol("bufferConcat"); + MAYBEEND = Symbol("maybeEnd"); + WRITING = Symbol("writing"); + ABORTED2 = Symbol("aborted"); + DONE = Symbol("onDone"); + SAW_VALID_ENTRY = Symbol("sawValidEntry"); + SAW_NULL_BLOCK = Symbol("sawNullBlock"); + SAW_EOF = Symbol("sawEOF"); + CLOSESTREAM = Symbol("closeStream"); + noop2 = () => true; + Parser = class extends import_events3.EventEmitter { + file; + strict; + maxMetaEntrySize; + filter; + brotli; + zstd; + writable = true; + readable = false; + [QUEUE] = []; + [BUFFER2]; + [READENTRY]; + [WRITEENTRY]; + [STATE] = "begin"; + [META] = ""; + [EX]; + [GEX]; + [ENDED] = false; + [UNZIP]; + [ABORTED2] = false; + [SAW_VALID_ENTRY]; + [SAW_NULL_BLOCK] = false; + [SAW_EOF] = false; + [WRITING] = false; + [CONSUMING] = false; + [EMITTEDEND] = false; + constructor(opt = {}) { + super(); + this.file = opt.file || ""; + this.on(DONE, () => { + if (this[STATE] === "begin" || this[SAW_VALID_ENTRY] === false) { + this.warn("TAR_BAD_ARCHIVE", "Unrecognized archive format"); + } + }); + if (opt.ondone) { + this.on(DONE, opt.ondone); + } else { + this.on(DONE, () => { + this.emit("prefinish"); + this.emit("finish"); + this.emit("end"); + }); } - const bodyLen = Buffer.byteLength(body); - const bufLen = 512 * Math.ceil(1 + bodyLen / 512); - const buf = Buffer.allocUnsafe(bufLen); - for (let i = 0; i < 512; i++) { - buf[i] = 0; + this.strict = !!opt.strict; + this.maxMetaEntrySize = opt.maxMetaEntrySize || maxMetaEntrySize; + this.filter = typeof opt.filter === "function" ? opt.filter : noop2; + const isTBR = opt.file && (opt.file.endsWith(".tar.br") || opt.file.endsWith(".tbr")); + this.brotli = !(opt.gzip || opt.zstd) && opt.brotli !== void 0 ? opt.brotli : isTBR ? void 0 : false; + const isTZST = opt.file && (opt.file.endsWith(".tar.zst") || opt.file.endsWith(".tzst")); + this.zstd = !(opt.gzip || opt.brotli) && opt.zstd !== void 0 ? opt.zstd : isTZST ? true : void 0; + this.on("end", () => this[CLOSESTREAM]()); + if (typeof opt.onwarn === "function") { + this.on("warn", opt.onwarn); } - new Header({ - // XXX split the path - // then the path should be PaxHeader + basename, but less than 99, - // prepend with the dirname - /* c8 ignore start */ - path: ("PaxHeader/" + (0, import_node_path2.basename)(this.path ?? "")).slice(0, 99), - /* c8 ignore stop */ - mode: this.mode || 420, - uid: this.uid, - gid: this.gid, - size: bodyLen, - mtime: this.mtime, - type: this.global ? "GlobalExtendedHeader" : "ExtendedHeader", - linkpath: "", - uname: this.uname || "", - gname: this.gname || "", - devmaj: 0, - devmin: 0, - atime: this.atime, - ctime: this.ctime - }).encode(buf); - buf.write(body, 512, bodyLen, "utf8"); - for (let i = bodyLen + 512; i < buf.length; i++) { - buf[i] = 0; + if (typeof opt.onReadEntry === "function") { + this.on("entry", opt.onReadEntry); } - return buf; } - encodeBody() { - return this.encodeField("path") + this.encodeField("ctime") + this.encodeField("atime") + this.encodeField("dev") + this.encodeField("ino") + this.encodeField("nlink") + this.encodeField("charset") + this.encodeField("comment") + this.encodeField("gid") + this.encodeField("gname") + this.encodeField("linkpath") + this.encodeField("mtime") + this.encodeField("size") + this.encodeField("uid") + this.encodeField("uname"); + warn(code2, message, data = {}) { + warnMethod(this, code2, message, data); } - encodeField(field) { - if (this[field] === void 0) { - return ""; + [CONSUMEHEADER](chunk, position) { + if (this[SAW_VALID_ENTRY] === void 0) { + this[SAW_VALID_ENTRY] = false; } - const r = this[field]; - const v = r instanceof Date ? r.getTime() / 1e3 : r; - const s = " " + (field === "dev" || field === "ino" || field === "nlink" ? "SCHILY." : "") + field + "=" + v + "\n"; - const byteLen = Buffer.byteLength(s); - let digits = Math.floor(Math.log(byteLen) / Math.log(10)) + 1; - if (byteLen + digits >= Math.pow(10, digits)) { - digits += 1; + let header; + try { + header = new Header(chunk, position, this[EX], this[GEX]); + } catch (er) { + return this.warn("TAR_ENTRY_INVALID", er); + } + if (header.nullBlock) { + if (this[SAW_NULL_BLOCK]) { + this[SAW_EOF] = true; + if (this[STATE] === "begin") { + this[STATE] = "header"; + } + this[EMIT]("eof"); + } else { + this[SAW_NULL_BLOCK] = true; + this[EMIT]("nullBlock"); + } + } else { + this[SAW_NULL_BLOCK] = false; + if (!header.cksumValid) { + this.warn("TAR_ENTRY_INVALID", "checksum failure", { header }); + } else if (!header.path) { + this.warn("TAR_ENTRY_INVALID", "path is required", { header }); + } else { + const type = header.type; + if (/^(Symbolic)?Link$/.test(type) && !header.linkpath) { + this.warn("TAR_ENTRY_INVALID", "linkpath required", { + header + }); + } else if (!/^(Symbolic)?Link$/.test(type) && !/^(Global)?ExtendedHeader$/.test(type) && header.linkpath) { + this.warn("TAR_ENTRY_INVALID", "linkpath forbidden", { + header + }); + } else { + const entry = this[WRITEENTRY] = new ReadEntry(header, this[EX], this[GEX]); + if (!this[SAW_VALID_ENTRY]) { + if (entry.remain) { + const onend = () => { + if (!entry.invalid) { + this[SAW_VALID_ENTRY] = true; + } + }; + entry.on("end", onend); + } else { + this[SAW_VALID_ENTRY] = true; + } + } + if (entry.meta) { + if (entry.size > this.maxMetaEntrySize) { + entry.ignore = true; + this[EMIT]("ignoredEntry", entry); + this[STATE] = "ignore"; + entry.resume(); + } else if (entry.size > 0) { + this[META] = ""; + entry.on("data", (c) => this[META] += c); + this[STATE] = "meta"; + } + } else { + this[EX] = void 0; + entry.ignore = entry.ignore || !this.filter(entry.path, entry); + if (entry.ignore) { + this[EMIT]("ignoredEntry", entry); + this[STATE] = entry.remain ? "ignore" : "header"; + entry.resume(); + } else { + if (entry.remain) { + this[STATE] = "body"; + } else { + this[STATE] = "header"; + entry.end(); + } + if (!this[READENTRY]) { + this[QUEUE].push(entry); + this[NEXTENTRY](); + } else { + this[QUEUE].push(entry); + } + } + } + } + } } - const len = digits + byteLen; - return len + s; } - static parse(str, ex, g = false) { - return new _Pax(merge(parseKV(str), ex), g); + [CLOSESTREAM]() { + queueMicrotask(() => this.emit("close")); } - }; - merge = (a, b) => b ? Object.assign({}, b, a) : a; - parseKV = (str) => str.replace(/\n$/, "").split("\n").reduce(parseKVLine, /* @__PURE__ */ Object.create(null)); - parseKVLine = (set, line) => { - const n = parseInt(line, 10); - if (n !== Buffer.byteLength(line) + 1) { - return set; - } - line = line.slice((n + " ").length); - const kv = line.split("="); - const r = kv.shift(); - if (!r) { - return set; - } - const k = r.replace(/^SCHILY\.(dev|ino|nlink)/, "$1"); - const v = kv.join("="); - set[k] = /^([A-Z]+\.)?([mac]|birth|creation)time$/.test(k) ? new Date(Number(v) * 1e3) : /^[0-9]+$/.test(v) ? +v : v; - return set; - }; - } -}); - -// .yarn/cache/tar-npm-7.4.3-1dbbd1ffc3-d4679609bb.zip/node_modules/tar/dist/esm/normalize-windows-path.js -var platform, normalizeWindowsPath; -var init_normalize_windows_path = __esm({ - ".yarn/cache/tar-npm-7.4.3-1dbbd1ffc3-d4679609bb.zip/node_modules/tar/dist/esm/normalize-windows-path.js"() { - platform = process.env.TESTING_TAR_FAKE_PLATFORM || process.platform; - normalizeWindowsPath = platform !== "win32" ? (p) => p : (p) => p && p.replace(/\\/g, "/"); - } -}); - -// .yarn/cache/tar-npm-7.4.3-1dbbd1ffc3-d4679609bb.zip/node_modules/tar/dist/esm/read-entry.js -var ReadEntry; -var init_read_entry = __esm({ - ".yarn/cache/tar-npm-7.4.3-1dbbd1ffc3-d4679609bb.zip/node_modules/tar/dist/esm/read-entry.js"() { - init_esm(); - init_normalize_windows_path(); - ReadEntry = class extends Minipass { - extended; - globalExtended; - header; - startBlockSize; - blockRemain; - remain; - type; - meta = false; - ignore = false; - path; - mode; - uid; - gid; - uname; - gname; - size = 0; - mtime; - atime; - ctime; - linkpath; - dev; - ino; - nlink; - invalid = false; - absolute; - unsupported = false; - constructor(header, ex, gex) { - super({}); - this.pause(); - this.extended = ex; - this.globalExtended = gex; - this.header = header; - this.remain = header.size ?? 0; - this.startBlockSize = 512 * Math.ceil(this.remain / 512); - this.blockRemain = this.startBlockSize; - this.type = header.type; - switch (this.type) { - case "File": - case "OldFile": - case "Link": - case "SymbolicLink": - case "CharacterDevice": - case "BlockDevice": - case "Directory": - case "FIFO": - case "ContiguousFile": - case "GNUDumpDir": - break; - case "NextFileHasLongLinkpath": - case "NextFileHasLongPath": - case "OldGnuLongPath": - case "GlobalExtendedHeader": - case "ExtendedHeader": - case "OldExtendedHeader": - this.meta = true; - break; - // NOTE: gnutar and bsdtar treat unrecognized types as 'File' - // it may be worth doing the same, but with a warning. - default: - this.ignore = true; - } - if (!header.path) { - throw new Error("no path provided for tar.ReadEntry"); - } - this.path = normalizeWindowsPath(header.path); - this.mode = header.mode; - if (this.mode) { - this.mode = this.mode & 4095; - } - this.uid = header.uid; - this.gid = header.gid; - this.uname = header.uname; - this.gname = header.gname; - this.size = this.remain; - this.mtime = header.mtime; - this.atime = header.atime; - this.ctime = header.ctime; - this.linkpath = header.linkpath ? normalizeWindowsPath(header.linkpath) : void 0; - this.uname = header.uname; - this.gname = header.gname; - if (ex) { - this.#slurp(ex); + [PROCESSENTRY](entry) { + let go = true; + if (!entry) { + this[READENTRY] = void 0; + go = false; + } else if (Array.isArray(entry)) { + const [ev, ...args] = entry; + this.emit(ev, ...args); + } else { + this[READENTRY] = entry; + this.emit("entry", entry); + if (!entry.emittedEnd) { + entry.on("end", () => this[NEXTENTRY]()); + go = false; + } } - if (gex) { - this.#slurp(gex, true); + return go; + } + [NEXTENTRY]() { + do { + } while (this[PROCESSENTRY](this[QUEUE].shift())); + if (!this[QUEUE].length) { + const re = this[READENTRY]; + const drainNow = !re || re.flowing || re.size === re.remain; + if (drainNow) { + if (!this[WRITING]) { + this.emit("drain"); + } + } else { + re.once("drain", () => this.emit("drain")); + } } } - write(data) { - const writeLen = data.length; - if (writeLen > this.blockRemain) { - throw new Error("writing more to entry than is appropriate"); + [CONSUMEBODY](chunk, position) { + const entry = this[WRITEENTRY]; + if (!entry) { + throw new Error("attempt to consume body without entry??"); } - const r = this.remain; - const br = this.blockRemain; - this.remain = Math.max(0, r - writeLen); - this.blockRemain = Math.max(0, br - writeLen); - if (this.ignore) { - return true; + const br = entry.blockRemain ?? 0; + const c = br >= chunk.length && position === 0 ? chunk : chunk.subarray(position, position + br); + entry.write(c); + if (!entry.blockRemain) { + this[STATE] = "header"; + this[WRITEENTRY] = void 0; + entry.end(); } - if (r >= writeLen) { - return super.write(data); + return c.length; + } + [CONSUMEMETA](chunk, position) { + const entry = this[WRITEENTRY]; + const ret = this[CONSUMEBODY](chunk, position); + if (!this[WRITEENTRY] && entry) { + this[EMITMETA](entry); } - return super.write(data.subarray(0, r)); + return ret; } - #slurp(ex, gex = false) { - if (ex.path) - ex.path = normalizeWindowsPath(ex.path); - if (ex.linkpath) - ex.linkpath = normalizeWindowsPath(ex.linkpath); - Object.assign(this, Object.fromEntries(Object.entries(ex).filter(([k, v]) => { - return !(v === null || v === void 0 || k === "path" && gex); - }))); + [EMIT](ev, data, extra) { + if (!this[QUEUE].length && !this[READENTRY]) { + this.emit(ev, data, extra); + } else { + this[QUEUE].push([ev, data, extra]); + } } - }; - } -}); - -// .yarn/cache/tar-npm-7.4.3-1dbbd1ffc3-d4679609bb.zip/node_modules/tar/dist/esm/warn-method.js -var warnMethod; -var init_warn_method = __esm({ - ".yarn/cache/tar-npm-7.4.3-1dbbd1ffc3-d4679609bb.zip/node_modules/tar/dist/esm/warn-method.js"() { - warnMethod = (self2, code2, message, data = {}) => { - if (self2.file) { - data.file = self2.file; + [EMITMETA](entry) { + this[EMIT]("meta", this[META]); + switch (entry.type) { + case "ExtendedHeader": + case "OldExtendedHeader": + this[EX] = Pax.parse(this[META], this[EX], false); + break; + case "GlobalExtendedHeader": + this[GEX] = Pax.parse(this[META], this[GEX], true); + break; + case "NextFileHasLongPath": + case "OldGnuLongPath": { + const ex = this[EX] ?? /* @__PURE__ */ Object.create(null); + this[EX] = ex; + ex.path = this[META].replace(/\0.*/, ""); + break; + } + case "NextFileHasLongLinkpath": { + const ex = this[EX] || /* @__PURE__ */ Object.create(null); + this[EX] = ex; + ex.linkpath = this[META].replace(/\0.*/, ""); + break; + } + /* c8 ignore start */ + default: + throw new Error("unknown meta: " + entry.type); + } } - if (self2.cwd) { - data.cwd = self2.cwd; + abort(error) { + this[ABORTED2] = true; + this.emit("abort", error); + this.warn("TAR_ABORT", error, { recoverable: false }); } - data.code = message instanceof Error && message.code || code2; - data.tarCode = code2; - if (!self2.strict && data.recoverable !== false) { - if (message instanceof Error) { - data = Object.assign(message, data); - message = message.message; + write(chunk, encoding, cb) { + if (typeof encoding === "function") { + cb = encoding; + encoding = void 0; } - self2.emit("warn", code2, message, data); - } else if (message instanceof Error) { - self2.emit("error", Object.assign(message, data)); - } else { - self2.emit("error", Object.assign(new Error(`${code2}: ${message}`), data)); - } - }; - } -}); - -// .yarn/cache/tar-npm-7.4.3-1dbbd1ffc3-d4679609bb.zip/node_modules/tar/dist/esm/parse.js -var import_events3, maxMetaEntrySize, gzipHeader, STATE, WRITEENTRY, READENTRY, NEXTENTRY, PROCESSENTRY, EX, GEX, META, EMITMETA, BUFFER2, QUEUE, ENDED, EMITTEDEND, EMIT, UNZIP, CONSUMECHUNK, CONSUMECHUNKSUB, CONSUMEBODY, CONSUMEMETA, CONSUMEHEADER, CONSUMING, BUFFERCONCAT, MAYBEEND, WRITING, ABORTED2, DONE, SAW_VALID_ENTRY, SAW_NULL_BLOCK, SAW_EOF, CLOSESTREAM, noop, Parser; -var init_parse = __esm({ - ".yarn/cache/tar-npm-7.4.3-1dbbd1ffc3-d4679609bb.zip/node_modules/tar/dist/esm/parse.js"() { - import_events3 = require("events"); - init_esm3(); - init_esm4(); - init_header(); - init_pax(); - init_read_entry(); - init_warn_method(); - maxMetaEntrySize = 1024 * 1024; - gzipHeader = Buffer.from([31, 139]); - STATE = Symbol("state"); - WRITEENTRY = Symbol("writeEntry"); - READENTRY = Symbol("readEntry"); - NEXTENTRY = Symbol("nextEntry"); - PROCESSENTRY = Symbol("processEntry"); - EX = Symbol("extendedHeader"); - GEX = Symbol("globalExtendedHeader"); - META = Symbol("meta"); - EMITMETA = Symbol("emitMeta"); - BUFFER2 = Symbol("buffer"); - QUEUE = Symbol("queue"); - ENDED = Symbol("ended"); - EMITTEDEND = Symbol("emittedEnd"); - EMIT = Symbol("emit"); - UNZIP = Symbol("unzip"); - CONSUMECHUNK = Symbol("consumeChunk"); - CONSUMECHUNKSUB = Symbol("consumeChunkSub"); - CONSUMEBODY = Symbol("consumeBody"); - CONSUMEMETA = Symbol("consumeMeta"); - CONSUMEHEADER = Symbol("consumeHeader"); - CONSUMING = Symbol("consuming"); - BUFFERCONCAT = Symbol("bufferConcat"); - MAYBEEND = Symbol("maybeEnd"); - WRITING = Symbol("writing"); - ABORTED2 = Symbol("aborted"); - DONE = Symbol("onDone"); - SAW_VALID_ENTRY = Symbol("sawValidEntry"); - SAW_NULL_BLOCK = Symbol("sawNullBlock"); - SAW_EOF = Symbol("sawEOF"); - CLOSESTREAM = Symbol("closeStream"); - noop = () => true; - Parser = class extends import_events3.EventEmitter { - file; - strict; - maxMetaEntrySize; - filter; - brotli; - writable = true; - readable = false; - [QUEUE] = new Yallist(); - [BUFFER2]; - [READENTRY]; - [WRITEENTRY]; - [STATE] = "begin"; - [META] = ""; - [EX]; - [GEX]; - [ENDED] = false; - [UNZIP]; - [ABORTED2] = false; - [SAW_VALID_ENTRY]; - [SAW_NULL_BLOCK] = false; - [SAW_EOF] = false; - [WRITING] = false; - [CONSUMING] = false; - [EMITTEDEND] = false; - constructor(opt = {}) { - super(); - this.file = opt.file || ""; - this.on(DONE, () => { - if (this[STATE] === "begin" || this[SAW_VALID_ENTRY] === false) { - this.warn("TAR_BAD_ARCHIVE", "Unrecognized archive format"); - } - }); - if (opt.ondone) { - this.on(DONE, opt.ondone); - } else { - this.on(DONE, () => { - this.emit("prefinish"); - this.emit("finish"); - this.emit("end"); - }); - } - this.strict = !!opt.strict; - this.maxMetaEntrySize = opt.maxMetaEntrySize || maxMetaEntrySize; - this.filter = typeof opt.filter === "function" ? opt.filter : noop; - const isTBR = opt.file && (opt.file.endsWith(".tar.br") || opt.file.endsWith(".tbr")); - this.brotli = !opt.gzip && opt.brotli !== void 0 ? opt.brotli : isTBR ? void 0 : false; - this.on("end", () => this[CLOSESTREAM]()); - if (typeof opt.onwarn === "function") { - this.on("warn", opt.onwarn); - } - if (typeof opt.onReadEntry === "function") { - this.on("entry", opt.onReadEntry); - } - } - warn(code2, message, data = {}) { - warnMethod(this, code2, message, data); - } - [CONSUMEHEADER](chunk, position) { - if (this[SAW_VALID_ENTRY] === void 0) { - this[SAW_VALID_ENTRY] = false; + if (typeof chunk === "string") { + chunk = Buffer.from( + chunk, + /* c8 ignore next */ + typeof encoding === "string" ? encoding : "utf8" + ); } - let header; - try { - header = new Header(chunk, position, this[EX], this[GEX]); - } catch (er) { - return this.warn("TAR_ENTRY_INVALID", er); + if (this[ABORTED2]) { + cb?.(); + return false; } - if (header.nullBlock) { - if (this[SAW_NULL_BLOCK]) { - this[SAW_EOF] = true; - if (this[STATE] === "begin") { - this[STATE] = "header"; + const needSniff = this[UNZIP] === void 0 || this.brotli === void 0 && this[UNZIP] === false; + if (needSniff && chunk) { + if (this[BUFFER2]) { + chunk = Buffer.concat([this[BUFFER2], chunk]); + this[BUFFER2] = void 0; + } + if (chunk.length < ZIP_HEADER_LEN) { + this[BUFFER2] = chunk; + cb?.(); + return true; + } + for (let i = 0; this[UNZIP] === void 0 && i < gzipHeader.length; i++) { + if (chunk[i] !== gzipHeader[i]) { + this[UNZIP] = false; } - this[EMIT]("eof"); - } else { - this[SAW_NULL_BLOCK] = true; - this[EMIT]("nullBlock"); } - } else { - this[SAW_NULL_BLOCK] = false; - if (!header.cksumValid) { - this.warn("TAR_ENTRY_INVALID", "checksum failure", { header }); - } else if (!header.path) { - this.warn("TAR_ENTRY_INVALID", "path is required", { header }); - } else { - const type = header.type; - if (/^(Symbolic)?Link$/.test(type) && !header.linkpath) { - this.warn("TAR_ENTRY_INVALID", "linkpath required", { - header - }); - } else if (!/^(Symbolic)?Link$/.test(type) && !/^(Global)?ExtendedHeader$/.test(type) && header.linkpath) { - this.warn("TAR_ENTRY_INVALID", "linkpath forbidden", { - header - }); - } else { - const entry = this[WRITEENTRY] = new ReadEntry(header, this[EX], this[GEX]); - if (!this[SAW_VALID_ENTRY]) { - if (entry.remain) { - const onend = () => { - if (!entry.invalid) { - this[SAW_VALID_ENTRY] = true; - } - }; - entry.on("end", onend); - } else { - this[SAW_VALID_ENTRY] = true; - } + let isZstd = false; + if (this[UNZIP] === false && this.zstd !== false) { + isZstd = true; + for (let i = 0; i < zstdHeader.length; i++) { + if (chunk[i] !== zstdHeader[i]) { + isZstd = false; + break; } - if (entry.meta) { - if (entry.size > this.maxMetaEntrySize) { - entry.ignore = true; - this[EMIT]("ignoredEntry", entry); - this[STATE] = "ignore"; - entry.resume(); - } else if (entry.size > 0) { - this[META] = ""; - entry.on("data", (c) => this[META] += c); - this[STATE] = "meta"; - } + } + } + const maybeBrotli = this.brotli === void 0 && !isZstd; + if (this[UNZIP] === false && maybeBrotli) { + if (chunk.length < 512) { + if (this[ENDED]) { + this.brotli = true; } else { - this[EX] = void 0; - entry.ignore = entry.ignore || !this.filter(entry.path, entry); - if (entry.ignore) { - this[EMIT]("ignoredEntry", entry); - this[STATE] = entry.remain ? "ignore" : "header"; - entry.resume(); - } else { - if (entry.remain) { - this[STATE] = "body"; - } else { - this[STATE] = "header"; - entry.end(); - } - if (!this[READENTRY]) { - this[QUEUE].push(entry); - this[NEXTENTRY](); - } else { - this[QUEUE].push(entry); - } - } + this[BUFFER2] = chunk; + cb?.(); + return true; + } + } else { + try { + new Header(chunk.subarray(0, 512)); + this.brotli = false; + } catch (_) { + this.brotli = true; } } } + if (this[UNZIP] === void 0 || this[UNZIP] === false && (this.brotli || isZstd)) { + const ended = this[ENDED]; + this[ENDED] = false; + this[UNZIP] = this[UNZIP] === void 0 ? new Unzip({}) : isZstd ? new ZstdDecompress({}) : new BrotliDecompress({}); + this[UNZIP].on("data", (chunk2) => this[CONSUMECHUNK](chunk2)); + this[UNZIP].on("error", (er) => this.abort(er)); + this[UNZIP].on("end", () => { + this[ENDED] = true; + this[CONSUMECHUNK](); + }); + this[WRITING] = true; + const ret2 = !!this[UNZIP][ended ? "end" : "write"](chunk); + this[WRITING] = false; + cb?.(); + return ret2; + } } - } - [CLOSESTREAM]() { - queueMicrotask(() => this.emit("close")); - } - [PROCESSENTRY](entry) { - let go = true; - if (!entry) { - this[READENTRY] = void 0; - go = false; - } else if (Array.isArray(entry)) { - const [ev, ...args] = entry; - this.emit(ev, ...args); + this[WRITING] = true; + if (this[UNZIP]) { + this[UNZIP].write(chunk); } else { - this[READENTRY] = entry; - this.emit("entry", entry); - if (!entry.emittedEnd) { - entry.on("end", () => this[NEXTENTRY]()); - go = false; - } + this[CONSUMECHUNK](chunk); } - return go; - } - [NEXTENTRY]() { - do { - } while (this[PROCESSENTRY](this[QUEUE].shift())); - if (!this[QUEUE].length) { - const re = this[READENTRY]; - const drainNow = !re || re.flowing || re.size === re.remain; - if (drainNow) { - if (!this[WRITING]) { - this.emit("drain"); - } - } else { - re.once("drain", () => this.emit("drain")); - } - } - } - [CONSUMEBODY](chunk, position) { - const entry = this[WRITEENTRY]; - if (!entry) { - throw new Error("attempt to consume body without entry??"); - } - const br = entry.blockRemain ?? 0; - const c = br >= chunk.length && position === 0 ? chunk : chunk.subarray(position, position + br); - entry.write(c); - if (!entry.blockRemain) { - this[STATE] = "header"; - this[WRITEENTRY] = void 0; - entry.end(); - } - return c.length; - } - [CONSUMEMETA](chunk, position) { - const entry = this[WRITEENTRY]; - const ret = this[CONSUMEBODY](chunk, position); - if (!this[WRITEENTRY] && entry) { - this[EMITMETA](entry); - } - return ret; - } - [EMIT](ev, data, extra) { - if (!this[QUEUE].length && !this[READENTRY]) { - this.emit(ev, data, extra); - } else { - this[QUEUE].push([ev, data, extra]); - } - } - [EMITMETA](entry) { - this[EMIT]("meta", this[META]); - switch (entry.type) { - case "ExtendedHeader": - case "OldExtendedHeader": - this[EX] = Pax.parse(this[META], this[EX], false); - break; - case "GlobalExtendedHeader": - this[GEX] = Pax.parse(this[META], this[GEX], true); - break; - case "NextFileHasLongPath": - case "OldGnuLongPath": { - const ex = this[EX] ?? /* @__PURE__ */ Object.create(null); - this[EX] = ex; - ex.path = this[META].replace(/\0.*/, ""); - break; - } - case "NextFileHasLongLinkpath": { - const ex = this[EX] || /* @__PURE__ */ Object.create(null); - this[EX] = ex; - ex.linkpath = this[META].replace(/\0.*/, ""); - break; - } - /* c8 ignore start */ - default: - throw new Error("unknown meta: " + entry.type); - } - } - abort(error) { - this[ABORTED2] = true; - this.emit("abort", error); - this.warn("TAR_ABORT", error, { recoverable: false }); - } - write(chunk, encoding, cb) { - if (typeof encoding === "function") { - cb = encoding; - encoding = void 0; - } - if (typeof chunk === "string") { - chunk = Buffer.from( - chunk, - /* c8 ignore next */ - typeof encoding === "string" ? encoding : "utf8" - ); - } - if (this[ABORTED2]) { - cb?.(); - return false; - } - const needSniff = this[UNZIP] === void 0 || this.brotli === void 0 && this[UNZIP] === false; - if (needSniff && chunk) { - if (this[BUFFER2]) { - chunk = Buffer.concat([this[BUFFER2], chunk]); - this[BUFFER2] = void 0; - } - if (chunk.length < gzipHeader.length) { - this[BUFFER2] = chunk; - cb?.(); - return true; - } - for (let i = 0; this[UNZIP] === void 0 && i < gzipHeader.length; i++) { - if (chunk[i] !== gzipHeader[i]) { - this[UNZIP] = false; - } - } - const maybeBrotli = this.brotli === void 0; - if (this[UNZIP] === false && maybeBrotli) { - if (chunk.length < 512) { - if (this[ENDED]) { - this.brotli = true; - } else { - this[BUFFER2] = chunk; - cb?.(); - return true; - } - } else { - try { - new Header(chunk.subarray(0, 512)); - this.brotli = false; - } catch (_) { - this.brotli = true; - } - } - } - if (this[UNZIP] === void 0 || this[UNZIP] === false && this.brotli) { - const ended = this[ENDED]; - this[ENDED] = false; - this[UNZIP] = this[UNZIP] === void 0 ? new Unzip({}) : new BrotliDecompress({}); - this[UNZIP].on("data", (chunk2) => this[CONSUMECHUNK](chunk2)); - this[UNZIP].on("error", (er) => this.abort(er)); - this[UNZIP].on("end", () => { - this[ENDED] = true; - this[CONSUMECHUNK](); - }); - this[WRITING] = true; - const ret2 = !!this[UNZIP][ended ? "end" : "write"](chunk); - this[WRITING] = false; - cb?.(); - return ret2; - } - } - this[WRITING] = true; - if (this[UNZIP]) { - this[UNZIP].write(chunk); - } else { - this[CONSUMECHUNK](chunk); - } - this[WRITING] = false; - const ret = this[QUEUE].length ? false : this[READENTRY] ? this[READENTRY].flowing : true; - if (!ret && !this[QUEUE].length) { - this[READENTRY]?.once("drain", () => this.emit("drain")); - } - cb?.(); - return ret; + this[WRITING] = false; + const ret = this[QUEUE].length ? false : this[READENTRY] ? this[READENTRY].flowing : true; + if (!ret && !this[QUEUE].length) { + this[READENTRY]?.once("drain", () => this.emit("drain")); + } + cb?.(); + return ret; } [BUFFERCONCAT](c) { if (c && !this[ABORTED2]) { @@ -15516,7 +15422,7 @@ var init_parse = __esm({ this[UNZIP].end(); } else { this[ENDED] = true; - if (this.brotli === void 0) + if (this.brotli === void 0 || this.zstd === void 0) chunk = chunk || Buffer.alloc(0); if (chunk) this.write(chunk); @@ -15529,10 +15435,10 @@ var init_parse = __esm({ } }); -// .yarn/cache/tar-npm-7.4.3-1dbbd1ffc3-d4679609bb.zip/node_modules/tar/dist/esm/strip-trailing-slashes.js +// .yarn/cache/tar-npm-7.5.1-7b414f7fec-0dad0596a6.zip/node_modules/tar/dist/esm/strip-trailing-slashes.js var stripTrailingSlashes; var init_strip_trailing_slashes = __esm({ - ".yarn/cache/tar-npm-7.4.3-1dbbd1ffc3-d4679609bb.zip/node_modules/tar/dist/esm/strip-trailing-slashes.js"() { + ".yarn/cache/tar-npm-7.5.1-7b414f7fec-0dad0596a6.zip/node_modules/tar/dist/esm/strip-trailing-slashes.js"() { stripTrailingSlashes = (str) => { let i = str.length - 1; let slashesStart = -1; @@ -15545,7 +15451,7 @@ var init_strip_trailing_slashes = __esm({ } }); -// .yarn/cache/tar-npm-7.4.3-1dbbd1ffc3-d4679609bb.zip/node_modules/tar/dist/esm/list.js +// .yarn/cache/tar-npm-7.5.1-7b414f7fec-0dad0596a6.zip/node_modules/tar/dist/esm/list.js var list_exports = {}; __export(list_exports, { filesFilter: () => filesFilter, @@ -15553,7 +15459,7 @@ __export(list_exports, { }); var import_node_fs, import_path2, onReadEntryFunction, filesFilter, listFileSync, listFile, list; var init_list = __esm({ - ".yarn/cache/tar-npm-7.4.3-1dbbd1ffc3-d4679609bb.zip/node_modules/tar/dist/esm/list.js"() { + ".yarn/cache/tar-npm-7.5.1-7b414f7fec-0dad0596a6.zip/node_modules/tar/dist/esm/list.js"() { init_esm2(); import_node_fs = __toESM(require("node:fs"), 1); import_path2 = require("path"); @@ -15593,15 +15499,17 @@ var init_list = __esm({ const file = opt.file; let fd; try { - const stat2 = import_node_fs.default.statSync(file); + fd = import_node_fs.default.openSync(file, "r"); + const stat = import_node_fs.default.fstatSync(fd); const readSize = opt.maxReadSize || 16 * 1024 * 1024; - if (stat2.size < readSize) { - p.end(import_node_fs.default.readFileSync(file)); + if (stat.size < readSize) { + const buf = Buffer.allocUnsafe(stat.size); + import_node_fs.default.readSync(fd, buf, 0, stat.size, 0); + p.end(buf); } else { let pos2 = 0; const buf = Buffer.allocUnsafe(readSize); - fd = import_node_fs.default.openSync(file, "r"); - while (pos2 < stat2.size) { + while (pos2 < stat.size) { const bytesRead = import_node_fs.default.readSync(fd, buf, 0, readSize, pos2); pos2 += bytesRead; p.write(buf.subarray(0, bytesRead)); @@ -15618,22 +15526,22 @@ var init_list = __esm({ } }; listFile = (opt, _files) => { - const parse5 = new Parser(opt); + const parse4 = new Parser(opt); const readSize = opt.maxReadSize || 16 * 1024 * 1024; const file = opt.file; - const p = new Promise((resolve2, reject) => { - parse5.on("error", reject); - parse5.on("end", resolve2); - import_node_fs.default.stat(file, (er, stat2) => { + const p = new Promise((resolve, reject) => { + parse4.on("error", reject); + parse4.on("end", resolve); + import_node_fs.default.stat(file, (er, stat) => { if (er) { reject(er); } else { const stream = new ReadStream(file, { readSize, - size: stat2.size + size: stat.size }); stream.on("error", reject); - stream.pipe(parse5); + stream.pipe(parse4); } }); }); @@ -15648,10 +15556,10 @@ var init_list = __esm({ } }); -// .yarn/cache/tar-npm-7.4.3-1dbbd1ffc3-d4679609bb.zip/node_modules/tar/dist/esm/get-write-flag.js +// .yarn/cache/tar-npm-7.5.1-7b414f7fec-0dad0596a6.zip/node_modules/tar/dist/esm/get-write-flag.js var import_fs3, platform2, isWindows, O_CREAT, O_TRUNC, O_WRONLY, UV_FS_O_FILEMAP, fMapEnabled, fMapLimit, fMapFlag, getWriteFlag; var init_get_write_flag = __esm({ - ".yarn/cache/tar-npm-7.4.3-1dbbd1ffc3-d4679609bb.zip/node_modules/tar/dist/esm/get-write-flag.js"() { + ".yarn/cache/tar-npm-7.5.1-7b414f7fec-0dad0596a6.zip/node_modules/tar/dist/esm/get-write-flag.js"() { import_fs3 = __toESM(require("fs"), 1); platform2 = process.env.__FAKE_PLATFORM__ || process.platform; isWindows = platform2 === "win32"; @@ -15666,7 +15574,7 @@ var init_get_write_flag = __esm({ // .yarn/cache/chownr-npm-3.0.0-5275e85d25-43925b8770.zip/node_modules/chownr/dist/esm/index.js var import_node_fs2, import_node_path3, lchownSync, chown, chownrKid, chownr, chownrKidSync, chownrSync; -var init_esm5 = __esm({ +var init_esm4 = __esm({ ".yarn/cache/chownr-npm-3.0.0-5275e85d25-43925b8770.zip/node_modules/chownr/dist/esm/index.js"() { import_node_fs2 = __toESM(require("node:fs"), 1); import_node_path3 = __toESM(require("node:path"), 1); @@ -15747,277 +15655,10 @@ var init_esm5 = __esm({ } }); -// .yarn/cache/mkdirp-npm-3.0.1-f94bfa769e-9f2b975e92.zip/node_modules/mkdirp/dist/mjs/opts-arg.js -var import_fs4, optsArg; -var init_opts_arg = __esm({ - ".yarn/cache/mkdirp-npm-3.0.1-f94bfa769e-9f2b975e92.zip/node_modules/mkdirp/dist/mjs/opts-arg.js"() { - import_fs4 = require("fs"); - optsArg = (opts) => { - if (!opts) { - opts = { mode: 511 }; - } else if (typeof opts === "object") { - opts = { mode: 511, ...opts }; - } else if (typeof opts === "number") { - opts = { mode: opts }; - } else if (typeof opts === "string") { - opts = { mode: parseInt(opts, 8) }; - } else { - throw new TypeError("invalid options argument"); - } - const resolved = opts; - const optsFs = opts.fs || {}; - opts.mkdir = opts.mkdir || optsFs.mkdir || import_fs4.mkdir; - opts.mkdirAsync = opts.mkdirAsync ? opts.mkdirAsync : async (path16, options) => { - return new Promise((res, rej) => resolved.mkdir(path16, options, (er, made) => er ? rej(er) : res(made))); - }; - opts.stat = opts.stat || optsFs.stat || import_fs4.stat; - opts.statAsync = opts.statAsync ? opts.statAsync : async (path16) => new Promise((res, rej) => resolved.stat(path16, (err, stats) => err ? rej(err) : res(stats))); - opts.statSync = opts.statSync || optsFs.statSync || import_fs4.statSync; - opts.mkdirSync = opts.mkdirSync || optsFs.mkdirSync || import_fs4.mkdirSync; - return resolved; - }; - } -}); - -// .yarn/cache/mkdirp-npm-3.0.1-f94bfa769e-9f2b975e92.zip/node_modules/mkdirp/dist/mjs/mkdirp-manual.js -var import_path3, mkdirpManualSync, mkdirpManual; -var init_mkdirp_manual = __esm({ - ".yarn/cache/mkdirp-npm-3.0.1-f94bfa769e-9f2b975e92.zip/node_modules/mkdirp/dist/mjs/mkdirp-manual.js"() { - import_path3 = require("path"); - init_opts_arg(); - mkdirpManualSync = (path16, options, made) => { - const parent = (0, import_path3.dirname)(path16); - const opts = { ...optsArg(options), recursive: false }; - if (parent === path16) { - try { - return opts.mkdirSync(path16, opts); - } catch (er) { - const fer = er; - if (fer && fer.code !== "EISDIR") { - throw er; - } - return; - } - } - try { - opts.mkdirSync(path16, opts); - return made || path16; - } catch (er) { - const fer = er; - if (fer && fer.code === "ENOENT") { - return mkdirpManualSync(path16, opts, mkdirpManualSync(parent, opts, made)); - } - if (fer && fer.code !== "EEXIST" && fer && fer.code !== "EROFS") { - throw er; - } - try { - if (!opts.statSync(path16).isDirectory()) - throw er; - } catch (_) { - throw er; - } - } - }; - mkdirpManual = Object.assign(async (path16, options, made) => { - const opts = optsArg(options); - opts.recursive = false; - const parent = (0, import_path3.dirname)(path16); - if (parent === path16) { - return opts.mkdirAsync(path16, opts).catch((er) => { - const fer = er; - if (fer && fer.code !== "EISDIR") { - throw er; - } - }); - } - return opts.mkdirAsync(path16, opts).then(() => made || path16, async (er) => { - const fer = er; - if (fer && fer.code === "ENOENT") { - return mkdirpManual(parent, opts).then((made2) => mkdirpManual(path16, opts, made2)); - } - if (fer && fer.code !== "EEXIST" && fer.code !== "EROFS") { - throw er; - } - return opts.statAsync(path16).then((st) => { - if (st.isDirectory()) { - return made; - } else { - throw er; - } - }, () => { - throw er; - }); - }); - }, { sync: mkdirpManualSync }); - } -}); - -// .yarn/cache/mkdirp-npm-3.0.1-f94bfa769e-9f2b975e92.zip/node_modules/mkdirp/dist/mjs/find-made.js -var import_path4, findMade, findMadeSync; -var init_find_made = __esm({ - ".yarn/cache/mkdirp-npm-3.0.1-f94bfa769e-9f2b975e92.zip/node_modules/mkdirp/dist/mjs/find-made.js"() { - import_path4 = require("path"); - findMade = async (opts, parent, path16) => { - if (path16 === parent) { - return; - } - return opts.statAsync(parent).then( - (st) => st.isDirectory() ? path16 : void 0, - // will fail later - // will fail later - (er) => { - const fer = er; - return fer && fer.code === "ENOENT" ? findMade(opts, (0, import_path4.dirname)(parent), parent) : void 0; - } - ); - }; - findMadeSync = (opts, parent, path16) => { - if (path16 === parent) { - return void 0; - } - try { - return opts.statSync(parent).isDirectory() ? path16 : void 0; - } catch (er) { - const fer = er; - return fer && fer.code === "ENOENT" ? findMadeSync(opts, (0, import_path4.dirname)(parent), parent) : void 0; - } - }; - } -}); - -// .yarn/cache/mkdirp-npm-3.0.1-f94bfa769e-9f2b975e92.zip/node_modules/mkdirp/dist/mjs/mkdirp-native.js -var import_path5, mkdirpNativeSync, mkdirpNative; -var init_mkdirp_native = __esm({ - ".yarn/cache/mkdirp-npm-3.0.1-f94bfa769e-9f2b975e92.zip/node_modules/mkdirp/dist/mjs/mkdirp-native.js"() { - import_path5 = require("path"); - init_find_made(); - init_mkdirp_manual(); - init_opts_arg(); - mkdirpNativeSync = (path16, options) => { - const opts = optsArg(options); - opts.recursive = true; - const parent = (0, import_path5.dirname)(path16); - if (parent === path16) { - return opts.mkdirSync(path16, opts); - } - const made = findMadeSync(opts, path16); - try { - opts.mkdirSync(path16, opts); - return made; - } catch (er) { - const fer = er; - if (fer && fer.code === "ENOENT") { - return mkdirpManualSync(path16, opts); - } else { - throw er; - } - } - }; - mkdirpNative = Object.assign(async (path16, options) => { - const opts = { ...optsArg(options), recursive: true }; - const parent = (0, import_path5.dirname)(path16); - if (parent === path16) { - return await opts.mkdirAsync(path16, opts); - } - return findMade(opts, path16).then((made) => opts.mkdirAsync(path16, opts).then((m) => made || m).catch((er) => { - const fer = er; - if (fer && fer.code === "ENOENT") { - return mkdirpManual(path16, opts); - } else { - throw er; - } - })); - }, { sync: mkdirpNativeSync }); - } -}); - -// .yarn/cache/mkdirp-npm-3.0.1-f94bfa769e-9f2b975e92.zip/node_modules/mkdirp/dist/mjs/path-arg.js -var import_path6, platform3, pathArg; -var init_path_arg = __esm({ - ".yarn/cache/mkdirp-npm-3.0.1-f94bfa769e-9f2b975e92.zip/node_modules/mkdirp/dist/mjs/path-arg.js"() { - import_path6 = require("path"); - platform3 = process.env.__TESTING_MKDIRP_PLATFORM__ || process.platform; - pathArg = (path16) => { - if (/\0/.test(path16)) { - throw Object.assign(new TypeError("path must be a string without null bytes"), { - path: path16, - code: "ERR_INVALID_ARG_VALUE" - }); - } - path16 = (0, import_path6.resolve)(path16); - if (platform3 === "win32") { - const badWinChars = /[*|"<>?:]/; - const { root } = (0, import_path6.parse)(path16); - if (badWinChars.test(path16.substring(root.length))) { - throw Object.assign(new Error("Illegal characters in path."), { - path: path16, - code: "EINVAL" - }); - } - } - return path16; - }; - } -}); - -// .yarn/cache/mkdirp-npm-3.0.1-f94bfa769e-9f2b975e92.zip/node_modules/mkdirp/dist/mjs/use-native.js -var import_fs5, version2, versArr, hasNative, useNativeSync, useNative; -var init_use_native = __esm({ - ".yarn/cache/mkdirp-npm-3.0.1-f94bfa769e-9f2b975e92.zip/node_modules/mkdirp/dist/mjs/use-native.js"() { - import_fs5 = require("fs"); - init_opts_arg(); - version2 = process.env.__TESTING_MKDIRP_NODE_VERSION__ || process.version; - versArr = version2.replace(/^v/, "").split("."); - hasNative = +versArr[0] > 10 || +versArr[0] === 10 && +versArr[1] >= 12; - useNativeSync = !hasNative ? () => false : (opts) => optsArg(opts).mkdirSync === import_fs5.mkdirSync; - useNative = Object.assign(!hasNative ? () => false : (opts) => optsArg(opts).mkdir === import_fs5.mkdir, { - sync: useNativeSync - }); - } -}); - -// .yarn/cache/mkdirp-npm-3.0.1-f94bfa769e-9f2b975e92.zip/node_modules/mkdirp/dist/mjs/index.js -var mkdirpSync, mkdirp; -var init_mjs = __esm({ - ".yarn/cache/mkdirp-npm-3.0.1-f94bfa769e-9f2b975e92.zip/node_modules/mkdirp/dist/mjs/index.js"() { - init_mkdirp_manual(); - init_mkdirp_native(); - init_opts_arg(); - init_path_arg(); - init_use_native(); - init_mkdirp_manual(); - init_mkdirp_native(); - init_use_native(); - mkdirpSync = (path16, opts) => { - path16 = pathArg(path16); - const resolved = optsArg(opts); - return useNativeSync(resolved) ? mkdirpNativeSync(path16, resolved) : mkdirpManualSync(path16, resolved); - }; - mkdirp = Object.assign(async (path16, opts) => { - path16 = pathArg(path16); - const resolved = optsArg(opts); - return useNative(resolved) ? mkdirpNative(path16, resolved) : mkdirpManual(path16, resolved); - }, { - mkdirpSync, - mkdirpNative, - mkdirpNativeSync, - mkdirpManual, - mkdirpManualSync, - sync: mkdirpSync, - native: mkdirpNative, - nativeSync: mkdirpNativeSync, - manual: mkdirpManual, - manualSync: mkdirpManualSync, - useNative, - useNativeSync - }); - } -}); - -// .yarn/cache/tar-npm-7.4.3-1dbbd1ffc3-d4679609bb.zip/node_modules/tar/dist/esm/cwd-error.js +// .yarn/cache/tar-npm-7.5.1-7b414f7fec-0dad0596a6.zip/node_modules/tar/dist/esm/cwd-error.js var CwdError; var init_cwd_error = __esm({ - ".yarn/cache/tar-npm-7.4.3-1dbbd1ffc3-d4679609bb.zip/node_modules/tar/dist/esm/cwd-error.js"() { + ".yarn/cache/tar-npm-7.5.1-7b414f7fec-0dad0596a6.zip/node_modules/tar/dist/esm/cwd-error.js"() { CwdError = class extends Error { path; code; @@ -16034,10 +15675,10 @@ var init_cwd_error = __esm({ } }); -// .yarn/cache/tar-npm-7.4.3-1dbbd1ffc3-d4679609bb.zip/node_modules/tar/dist/esm/symlink-error.js +// .yarn/cache/tar-npm-7.5.1-7b414f7fec-0dad0596a6.zip/node_modules/tar/dist/esm/symlink-error.js var SymlinkError; var init_symlink_error = __esm({ - ".yarn/cache/tar-npm-7.4.3-1dbbd1ffc3-d4679609bb.zip/node_modules/tar/dist/esm/symlink-error.js"() { + ".yarn/cache/tar-npm-7.5.1-7b414f7fec-0dad0596a6.zip/node_modules/tar/dist/esm/symlink-error.js"() { SymlinkError = class extends Error { path; symlink; @@ -16055,28 +15696,26 @@ var init_symlink_error = __esm({ } }); -// .yarn/cache/tar-npm-7.4.3-1dbbd1ffc3-d4679609bb.zip/node_modules/tar/dist/esm/mkdir.js -var import_fs6, import_node_path4, cGet, cSet, checkCwd, mkdir3, mkdir_, onmkdir, checkCwdSync, mkdirSync4; +// .yarn/cache/tar-npm-7.5.1-7b414f7fec-0dad0596a6.zip/node_modules/tar/dist/esm/mkdir.js +var import_node_fs3, import_promises, import_node_path4, checkCwd, mkdir, mkdir_, onmkdir, checkCwdSync, mkdirSync2; var init_mkdir = __esm({ - ".yarn/cache/tar-npm-7.4.3-1dbbd1ffc3-d4679609bb.zip/node_modules/tar/dist/esm/mkdir.js"() { - init_esm5(); - import_fs6 = __toESM(require("fs"), 1); - init_mjs(); + ".yarn/cache/tar-npm-7.5.1-7b414f7fec-0dad0596a6.zip/node_modules/tar/dist/esm/mkdir.js"() { + init_esm4(); + import_node_fs3 = __toESM(require("node:fs"), 1); + import_promises = __toESM(require("node:fs/promises"), 1); import_node_path4 = __toESM(require("node:path"), 1); init_cwd_error(); init_normalize_windows_path(); init_symlink_error(); - cGet = (cache, key) => cache.get(normalizeWindowsPath(key)); - cSet = (cache, key, val) => cache.set(normalizeWindowsPath(key), val); checkCwd = (dir, cb) => { - import_fs6.default.stat(dir, (er, st) => { + import_node_fs3.default.stat(dir, (er, st) => { if (er || !st.isDirectory()) { er = new CwdError(dir, er?.code || "ENOTDIR"); } cb(er); }); }; - mkdir3 = (dir, opt, cb) => { + mkdir = (dir, opt, cb) => { dir = normalizeWindowsPath(dir); const umask = opt.umask ?? 18; const mode = opt.mode | 448; @@ -16086,30 +15725,25 @@ var init_mkdir = __esm({ const doChown = typeof uid === "number" && typeof gid === "number" && (uid !== opt.processUid || gid !== opt.processGid); const preserve = opt.preserve; const unlink = opt.unlink; - const cache = opt.cache; const cwd = normalizeWindowsPath(opt.cwd); const done = (er, created) => { if (er) { cb(er); } else { - cSet(cache, dir, true); if (created && doChown) { chownr(created, uid, gid, (er2) => done(er2)); } else if (needChmod) { - import_fs6.default.chmod(dir, mode, cb); + import_node_fs3.default.chmod(dir, mode, cb); } else { cb(); } } }; - if (cache && cGet(cache, dir) === true) { - return done(); - } if (dir === cwd) { return checkCwd(dir, done); } if (preserve) { - return mkdirp(dir, { mode }).then( + return import_promises.default.mkdir(dir, { mode, recursive: true }).then( (made) => done(null, made ?? void 0), // oh, ts done @@ -16117,33 +15751,30 @@ var init_mkdir = __esm({ } const sub = normalizeWindowsPath(import_node_path4.default.relative(cwd, dir)); const parts = sub.split("/"); - mkdir_(cwd, parts, mode, cache, unlink, cwd, void 0, done); + mkdir_(cwd, parts, mode, unlink, cwd, void 0, done); }; - mkdir_ = (base, parts, mode, cache, unlink, cwd, created, cb) => { + mkdir_ = (base, parts, mode, unlink, cwd, created, cb) => { if (!parts.length) { return cb(null, created); } const p = parts.shift(); const part = normalizeWindowsPath(import_node_path4.default.resolve(base + "/" + p)); - if (cGet(cache, part)) { - return mkdir_(part, parts, mode, cache, unlink, cwd, created, cb); - } - import_fs6.default.mkdir(part, mode, onmkdir(part, parts, mode, cache, unlink, cwd, created, cb)); + import_node_fs3.default.mkdir(part, mode, onmkdir(part, parts, mode, unlink, cwd, created, cb)); }; - onmkdir = (part, parts, mode, cache, unlink, cwd, created, cb) => (er) => { + onmkdir = (part, parts, mode, unlink, cwd, created, cb) => (er) => { if (er) { - import_fs6.default.lstat(part, (statEr, st) => { + import_node_fs3.default.lstat(part, (statEr, st) => { if (statEr) { statEr.path = statEr.path && normalizeWindowsPath(statEr.path); cb(statEr); } else if (st.isDirectory()) { - mkdir_(part, parts, mode, cache, unlink, cwd, created, cb); + mkdir_(part, parts, mode, unlink, cwd, created, cb); } else if (unlink) { - import_fs6.default.unlink(part, (er2) => { + import_node_fs3.default.unlink(part, (er2) => { if (er2) { return cb(er2); } - import_fs6.default.mkdir(part, mode, onmkdir(part, parts, mode, cache, unlink, cwd, created, cb)); + import_node_fs3.default.mkdir(part, mode, onmkdir(part, parts, mode, unlink, cwd, created, cb)); }); } else if (st.isSymbolicLink()) { return cb(new SymlinkError(part, part + "/" + parts.join("/"))); @@ -16153,14 +15784,14 @@ var init_mkdir = __esm({ }); } else { created = created || part; - mkdir_(part, parts, mode, cache, unlink, cwd, created, cb); + mkdir_(part, parts, mode, unlink, cwd, created, cb); } }; checkCwdSync = (dir) => { let ok = false; let code2 = void 0; try { - ok = import_fs6.default.statSync(dir).isDirectory(); + ok = import_node_fs3.default.statSync(dir).isDirectory(); } catch (er) { code2 = er?.code; } finally { @@ -16169,7 +15800,7 @@ var init_mkdir = __esm({ } } }; - mkdirSync4 = (dir, opt) => { + mkdirSync2 = (dir, opt) => { dir = normalizeWindowsPath(dir); const umask = opt.umask ?? 18; const mode = opt.mode | 448; @@ -16179,49 +15810,38 @@ var init_mkdir = __esm({ const doChown = typeof uid === "number" && typeof gid === "number" && (uid !== opt.processUid || gid !== opt.processGid); const preserve = opt.preserve; const unlink = opt.unlink; - const cache = opt.cache; const cwd = normalizeWindowsPath(opt.cwd); const done = (created2) => { - cSet(cache, dir, true); if (created2 && doChown) { chownrSync(created2, uid, gid); } if (needChmod) { - import_fs6.default.chmodSync(dir, mode); + import_node_fs3.default.chmodSync(dir, mode); } }; - if (cache && cGet(cache, dir) === true) { - return done(); - } if (dir === cwd) { checkCwdSync(cwd); return done(); } if (preserve) { - return done(mkdirpSync(dir, mode) ?? void 0); + return done(import_node_fs3.default.mkdirSync(dir, { mode, recursive: true }) ?? void 0); } const sub = normalizeWindowsPath(import_node_path4.default.relative(cwd, dir)); const parts = sub.split("/"); let created = void 0; for (let p = parts.shift(), part = cwd; p && (part += "/" + p); p = parts.shift()) { part = normalizeWindowsPath(import_node_path4.default.resolve(part)); - if (cGet(cache, part)) { - continue; - } try { - import_fs6.default.mkdirSync(part, mode); + import_node_fs3.default.mkdirSync(part, mode); created = created || part; - cSet(cache, part, true); } catch (er) { - const st = import_fs6.default.lstatSync(part); + const st = import_node_fs3.default.lstatSync(part); if (st.isDirectory()) { - cSet(cache, part, true); continue; } else if (unlink) { - import_fs6.default.unlinkSync(part); - import_fs6.default.mkdirSync(part, mode); + import_node_fs3.default.unlinkSync(part); + import_node_fs3.default.mkdirSync(part, mode); created = created || part; - cSet(cache, part, true); continue; } else if (st.isSymbolicLink()) { return new SymlinkError(part, part + "/" + parts.join("/")); @@ -16233,45 +15853,30 @@ var init_mkdir = __esm({ } }); -// .yarn/cache/tar-npm-7.4.3-1dbbd1ffc3-d4679609bb.zip/node_modules/tar/dist/esm/normalize-unicode.js -var normalizeCache, hasOwnProperty, normalizeUnicode; -var init_normalize_unicode = __esm({ - ".yarn/cache/tar-npm-7.4.3-1dbbd1ffc3-d4679609bb.zip/node_modules/tar/dist/esm/normalize-unicode.js"() { - normalizeCache = /* @__PURE__ */ Object.create(null); - ({ hasOwnProperty } = Object.prototype); - normalizeUnicode = (s) => { - if (!hasOwnProperty.call(normalizeCache, s)) { - normalizeCache[s] = s.normalize("NFD"); - } - return normalizeCache[s]; - }; - } -}); - -// .yarn/cache/tar-npm-7.4.3-1dbbd1ffc3-d4679609bb.zip/node_modules/tar/dist/esm/strip-absolute-path.js -var import_node_path5, isAbsolute, parse4, stripAbsolutePath; +// .yarn/cache/tar-npm-7.5.1-7b414f7fec-0dad0596a6.zip/node_modules/tar/dist/esm/strip-absolute-path.js +var import_node_path5, isAbsolute, parse3, stripAbsolutePath; var init_strip_absolute_path = __esm({ - ".yarn/cache/tar-npm-7.4.3-1dbbd1ffc3-d4679609bb.zip/node_modules/tar/dist/esm/strip-absolute-path.js"() { + ".yarn/cache/tar-npm-7.5.1-7b414f7fec-0dad0596a6.zip/node_modules/tar/dist/esm/strip-absolute-path.js"() { import_node_path5 = require("node:path"); - ({ isAbsolute, parse: parse4 } = import_node_path5.win32); + ({ isAbsolute, parse: parse3 } = import_node_path5.win32); stripAbsolutePath = (path16) => { let r = ""; - let parsed = parse4(path16); + let parsed = parse3(path16); while (isAbsolute(path16) || parsed.root) { const root = path16.charAt(0) === "/" && path16.slice(0, 4) !== "//?/" ? "/" : parsed.root; path16 = path16.slice(root.length); r += root; - parsed = parse4(path16); + parsed = parse3(path16); } return [r, path16]; }; } }); -// .yarn/cache/tar-npm-7.4.3-1dbbd1ffc3-d4679609bb.zip/node_modules/tar/dist/esm/winchars.js +// .yarn/cache/tar-npm-7.5.1-7b414f7fec-0dad0596a6.zip/node_modules/tar/dist/esm/winchars.js var raw, win, toWin, toRaw, encode2, decode; var init_winchars = __esm({ - ".yarn/cache/tar-npm-7.4.3-1dbbd1ffc3-d4679609bb.zip/node_modules/tar/dist/esm/winchars.js"() { + ".yarn/cache/tar-npm-7.5.1-7b414f7fec-0dad0596a6.zip/node_modules/tar/dist/esm/winchars.js"() { raw = ["|", "<", ">", "?", ":"]; win = raw.map((char) => String.fromCharCode(61440 + char.charCodeAt(0))); toWin = new Map(raw.map((char, i) => [char, win[i]])); @@ -16281,15 +15886,44 @@ var init_winchars = __esm({ } }); -// .yarn/cache/tar-npm-7.4.3-1dbbd1ffc3-d4679609bb.zip/node_modules/tar/dist/esm/path-reservations.js -var import_node_path6, platform4, isWindows2, getDirs, PathReservations; -var init_path_reservations = __esm({ - ".yarn/cache/tar-npm-7.4.3-1dbbd1ffc3-d4679609bb.zip/node_modules/tar/dist/esm/path-reservations.js"() { +// .yarn/cache/tar-npm-7.5.1-7b414f7fec-0dad0596a6.zip/node_modules/tar/dist/esm/normalize-unicode.js +var normalizeCache, MAX, cache, normalizeUnicode; +var init_normalize_unicode = __esm({ + ".yarn/cache/tar-npm-7.5.1-7b414f7fec-0dad0596a6.zip/node_modules/tar/dist/esm/normalize-unicode.js"() { + normalizeCache = /* @__PURE__ */ Object.create(null); + MAX = 1e4; + cache = /* @__PURE__ */ new Set(); + normalizeUnicode = (s) => { + if (!cache.has(s)) { + normalizeCache[s] = s.normalize("NFD"); + } else { + cache.delete(s); + } + cache.add(s); + const ret = normalizeCache[s]; + let i = cache.size - MAX; + if (i > MAX / 10) { + for (const s2 of cache) { + cache.delete(s2); + delete normalizeCache[s2]; + if (--i <= 0) + break; + } + } + return ret; + }; + } +}); + +// .yarn/cache/tar-npm-7.5.1-7b414f7fec-0dad0596a6.zip/node_modules/tar/dist/esm/path-reservations.js +var import_node_path6, platform3, isWindows2, getDirs, PathReservations; +var init_path_reservations = __esm({ + ".yarn/cache/tar-npm-7.5.1-7b414f7fec-0dad0596a6.zip/node_modules/tar/dist/esm/path-reservations.js"() { import_node_path6 = require("node:path"); init_normalize_unicode(); init_strip_trailing_slashes(); - platform4 = process.env.TESTING_TAR_FAKE_PLATFORM || process.platform; - isWindows2 = platform4 === "win32"; + platform3 = process.env.TESTING_TAR_FAKE_PLATFORM || process.platform; + isWindows2 = platform3 === "win32"; getDirs = (path16) => { const dirs = path16.split("/").slice(0, -1).reduce((set, path17) => { const s = set[set.length - 1]; @@ -16421,28 +16055,25 @@ var init_path_reservations = __esm({ } }); -// .yarn/cache/tar-npm-7.4.3-1dbbd1ffc3-d4679609bb.zip/node_modules/tar/dist/esm/unpack.js -var import_node_assert, import_node_crypto, import_node_fs3, import_node_path7, ONENTRY, CHECKFS, CHECKFS2, PRUNECACHE, ISREUSABLE, MAKEFS, FILE, DIRECTORY, LINK, SYMLINK, HARDLINK, UNSUPPORTED, CHECKPATH, MKDIR, ONERROR, PENDING, PEND, UNPEND, ENDED2, MAYBECLOSE, SKIP, DOCHOWN, UID, GID, CHECKED_CWD, platform5, isWindows3, DEFAULT_MAX_DEPTH, unlinkFile, unlinkFileSync, uint32, cacheKeyNormalize, pruneCache, dropCache, Unpack, callSync, UnpackSync; +// .yarn/cache/tar-npm-7.5.1-7b414f7fec-0dad0596a6.zip/node_modules/tar/dist/esm/unpack.js +var import_node_assert, import_node_crypto, import_node_fs4, import_node_path7, ONENTRY, CHECKFS, CHECKFS2, ISREUSABLE, MAKEFS, FILE, DIRECTORY, LINK, SYMLINK, HARDLINK, UNSUPPORTED, CHECKPATH, MKDIR, ONERROR, PENDING, PEND, UNPEND, ENDED2, MAYBECLOSE, SKIP, DOCHOWN, UID, GID, CHECKED_CWD, platform4, isWindows3, DEFAULT_MAX_DEPTH, unlinkFile, unlinkFileSync, uint32, Unpack, callSync, UnpackSync; var init_unpack = __esm({ - ".yarn/cache/tar-npm-7.4.3-1dbbd1ffc3-d4679609bb.zip/node_modules/tar/dist/esm/unpack.js"() { + ".yarn/cache/tar-npm-7.5.1-7b414f7fec-0dad0596a6.zip/node_modules/tar/dist/esm/unpack.js"() { init_esm2(); import_node_assert = __toESM(require("node:assert"), 1); import_node_crypto = require("node:crypto"); - import_node_fs3 = __toESM(require("node:fs"), 1); + import_node_fs4 = __toESM(require("node:fs"), 1); import_node_path7 = __toESM(require("node:path"), 1); init_get_write_flag(); init_mkdir(); - init_normalize_unicode(); init_normalize_windows_path(); init_parse(); init_strip_absolute_path(); - init_strip_trailing_slashes(); init_winchars(); init_path_reservations(); ONENTRY = Symbol("onEntry"); CHECKFS = Symbol("checkFs"); CHECKFS2 = Symbol("checkFs2"); - PRUNECACHE = Symbol("pruneCache"); ISREUSABLE = Symbol("isReusable"); MAKEFS = Symbol("makeFs"); FILE = Symbol("file"); @@ -16464,45 +16095,30 @@ var init_unpack = __esm({ UID = Symbol("uid"); GID = Symbol("gid"); CHECKED_CWD = Symbol("checkedCwd"); - platform5 = process.env.TESTING_TAR_FAKE_PLATFORM || process.platform; - isWindows3 = platform5 === "win32"; + platform4 = process.env.TESTING_TAR_FAKE_PLATFORM || process.platform; + isWindows3 = platform4 === "win32"; DEFAULT_MAX_DEPTH = 1024; unlinkFile = (path16, cb) => { if (!isWindows3) { - return import_node_fs3.default.unlink(path16, cb); + return import_node_fs4.default.unlink(path16, cb); } const name2 = path16 + ".DELETE." + (0, import_node_crypto.randomBytes)(16).toString("hex"); - import_node_fs3.default.rename(path16, name2, (er) => { + import_node_fs4.default.rename(path16, name2, (er) => { if (er) { return cb(er); } - import_node_fs3.default.unlink(name2, cb); + import_node_fs4.default.unlink(name2, cb); }); }; unlinkFileSync = (path16) => { if (!isWindows3) { - return import_node_fs3.default.unlinkSync(path16); + return import_node_fs4.default.unlinkSync(path16); } const name2 = path16 + ".DELETE." + (0, import_node_crypto.randomBytes)(16).toString("hex"); - import_node_fs3.default.renameSync(path16, name2); - import_node_fs3.default.unlinkSync(name2); + import_node_fs4.default.renameSync(path16, name2); + import_node_fs4.default.unlinkSync(name2); }; uint32 = (a, b, c) => a !== void 0 && a === a >>> 0 ? a : b !== void 0 && b === b >>> 0 ? b : c; - cacheKeyNormalize = (path16) => stripTrailingSlashes(normalizeWindowsPath(normalizeUnicode(path16))).toLowerCase(); - pruneCache = (cache, abs) => { - abs = cacheKeyNormalize(abs); - for (const path16 of cache.keys()) { - const pnorm = cacheKeyNormalize(path16); - if (pnorm === abs || pnorm.indexOf(abs + "/") === 0) { - cache.delete(path16); - } - } - }; - dropCache = (cache) => { - for (const key of cache.keys()) { - cache.delete(key); - } - }; Unpack = class extends Parser { [ENDED2] = false; [CHECKED_CWD] = false; @@ -16511,7 +16127,6 @@ var init_unpack = __esm({ transform; writable = true; readable = false; - dirCache; uid; gid; setOwner; @@ -16540,7 +16155,6 @@ var init_unpack = __esm({ }; super(opt); this.transform = opt.transform; - this.dirCache = opt.dirCache || /* @__PURE__ */ new Map(); this.chmod = !!opt.chmod; if (typeof opt.uid === "number" || typeof opt.gid === "number") { if (typeof opt.uid !== "number" || typeof opt.gid !== "number") { @@ -16701,7 +16315,7 @@ var init_unpack = __esm({ } } [MKDIR](dir, mode, cb) { - mkdir3(normalizeWindowsPath(dir), { + mkdir(normalizeWindowsPath(dir), { uid: this.uid, gid: this.gid, processUid: this.processUid, @@ -16709,7 +16323,6 @@ var init_unpack = __esm({ umask: this.processUmask, preserve: this.preservePaths, unlink: this.unlink, - cache: this.dirCache, cwd: this.cwd, mode }, cb); @@ -16733,7 +16346,7 @@ var init_unpack = __esm({ }); stream.on("error", (er) => { if (stream.fd) { - import_node_fs3.default.close(stream.fd, () => { + import_node_fs4.default.close(stream.fd, () => { }); } stream.write = () => true; @@ -16744,7 +16357,7 @@ var init_unpack = __esm({ const done = (er) => { if (er) { if (stream.fd) { - import_node_fs3.default.close(stream.fd, () => { + import_node_fs4.default.close(stream.fd, () => { }); } this[ONERROR](er, entry); @@ -16753,7 +16366,7 @@ var init_unpack = __esm({ } if (--actions === 0) { if (stream.fd !== void 0) { - import_node_fs3.default.close(stream.fd, (er2) => { + import_node_fs4.default.close(stream.fd, (er2) => { if (er2) { this[ONERROR](er2, entry); } else { @@ -16771,14 +16384,14 @@ var init_unpack = __esm({ actions++; const atime = entry.atime || /* @__PURE__ */ new Date(); const mtime = entry.mtime; - import_node_fs3.default.futimes(fd, atime, mtime, (er) => er ? import_node_fs3.default.utimes(abs, atime, mtime, (er2) => done(er2 && er)) : done()); + import_node_fs4.default.futimes(fd, atime, mtime, (er) => er ? import_node_fs4.default.utimes(abs, atime, mtime, (er2) => done(er2 && er)) : done()); } if (typeof fd === "number" && this[DOCHOWN](entry)) { actions++; const uid = this[UID](entry); const gid = this[GID](entry); if (typeof uid === "number" && typeof gid === "number") { - import_node_fs3.default.fchown(fd, uid, gid, (er) => er ? import_node_fs3.default.chown(abs, uid, gid, (er2) => done(er2 && er)) : done()); + import_node_fs4.default.fchown(fd, uid, gid, (er) => er ? import_node_fs4.default.chown(abs, uid, gid, (er2) => done(er2 && er)) : done()); } } done(); @@ -16811,11 +16424,11 @@ var init_unpack = __esm({ }; if (entry.mtime && !this.noMtime) { actions++; - import_node_fs3.default.utimes(String(entry.absolute), entry.atime || /* @__PURE__ */ new Date(), entry.mtime, done); + import_node_fs4.default.utimes(String(entry.absolute), entry.atime || /* @__PURE__ */ new Date(), entry.mtime, done); } if (this[DOCHOWN](entry)) { actions++; - import_node_fs3.default.chown(String(entry.absolute), Number(this[UID](entry)), Number(this[GID](entry)), done); + import_node_fs4.default.chown(String(entry.absolute), Number(this[UID](entry)), Number(this[GID](entry)), done); } done(); }); @@ -16858,17 +16471,8 @@ var init_unpack = __esm({ } this.reservations.reserve(paths, (done) => this[CHECKFS2](entry, done)); } - [PRUNECACHE](entry) { - if (entry.type === "SymbolicLink") { - dropCache(this.dirCache); - } else if (entry.type !== "Directory") { - pruneCache(this.dirCache, String(entry.absolute)); - } - } [CHECKFS2](entry, fullyDone) { - this[PRUNECACHE](entry); const done = (er) => { - this[PRUNECACHE](entry); fullyDone(er); }; const checkCwd2 = () => { @@ -16899,7 +16503,7 @@ var init_unpack = __esm({ afterMakeParent(); }; const afterMakeParent = () => { - import_node_fs3.default.lstat(String(entry.absolute), (lstatEr, st) => { + import_node_fs4.default.lstat(String(entry.absolute), (lstatEr, st) => { if (st && (this.keep || /* c8 ignore next */ this.newer && st.mtime > (entry.mtime ?? st.mtime))) { this[SKIP](entry); @@ -16916,10 +16520,10 @@ var init_unpack = __esm({ if (!needChmod) { return afterChmod(); } - return import_node_fs3.default.chmod(String(entry.absolute), Number(entry.mode), afterChmod); + return import_node_fs4.default.chmod(String(entry.absolute), Number(entry.mode), afterChmod); } if (entry.absolute !== this.cwd) { - return import_node_fs3.default.rmdir(String(entry.absolute), (er) => this[MAKEFS](er ?? null, entry, done)); + return import_node_fs4.default.rmdir(String(entry.absolute), (er) => this[MAKEFS](er ?? null, entry, done)); } } if (entry.absolute === this.cwd) { @@ -16955,7 +16559,7 @@ var init_unpack = __esm({ } } [LINK](entry, linkpath, link, done) { - import_node_fs3.default[link](linkpath, String(entry.absolute), (er) => { + import_node_fs4.default[link](linkpath, String(entry.absolute), (er) => { if (er) { this[ONERROR](er, entry); } else { @@ -16980,7 +16584,6 @@ var init_unpack = __esm({ }); } [CHECKFS](entry) { - this[PRUNECACHE](entry); if (!this[CHECKED_CWD]) { const er2 = this[MKDIR](this.cwd, this.dmode); if (er2) { @@ -16997,7 +16600,7 @@ var init_unpack = __esm({ } } } - const [lstatEr, st] = callSync(() => import_node_fs3.default.lstatSync(String(entry.absolute))); + const [lstatEr, st] = callSync(() => import_node_fs4.default.lstatSync(String(entry.absolute))); if (st && (this.keep || /* c8 ignore next */ this.newer && st.mtime > (entry.mtime ?? st.mtime))) { return this[SKIP](entry); @@ -17009,11 +16612,11 @@ var init_unpack = __esm({ if (entry.type === "Directory") { const needChmod = this.chmod && entry.mode && (st.mode & 4095) !== entry.mode; const [er3] = needChmod ? callSync(() => { - import_node_fs3.default.chmodSync(String(entry.absolute), Number(entry.mode)); + import_node_fs4.default.chmodSync(String(entry.absolute), Number(entry.mode)); }) : []; return this[MAKEFS](er3, entry); } - const [er2] = callSync(() => import_node_fs3.default.rmdirSync(String(entry.absolute))); + const [er2] = callSync(() => import_node_fs4.default.rmdirSync(String(entry.absolute))); this[MAKEFS](er2, entry); } const [er] = entry.absolute === this.cwd ? [] : callSync(() => unlinkFileSync(String(entry.absolute))); @@ -17024,7 +16627,7 @@ var init_unpack = __esm({ const oner = (er) => { let closeError; try { - import_node_fs3.default.closeSync(fd); + import_node_fs4.default.closeSync(fd); } catch (e) { closeError = e; } @@ -17035,7 +16638,7 @@ var init_unpack = __esm({ }; let fd; try { - fd = import_node_fs3.default.openSync(String(entry.absolute), getWriteFlag(entry.size), mode); + fd = import_node_fs4.default.openSync(String(entry.absolute), getWriteFlag(entry.size), mode); } catch (er) { return oner(er); } @@ -17046,7 +16649,7 @@ var init_unpack = __esm({ } tx.on("data", (chunk) => { try { - import_node_fs3.default.writeSync(fd, chunk, 0, chunk.length); + import_node_fs4.default.writeSync(fd, chunk, 0, chunk.length); } catch (er) { oner(er); } @@ -17057,10 +16660,10 @@ var init_unpack = __esm({ const atime = entry.atime || /* @__PURE__ */ new Date(); const mtime = entry.mtime; try { - import_node_fs3.default.futimesSync(fd, atime, mtime); + import_node_fs4.default.futimesSync(fd, atime, mtime); } catch (futimeser) { try { - import_node_fs3.default.utimesSync(String(entry.absolute), atime, mtime); + import_node_fs4.default.utimesSync(String(entry.absolute), atime, mtime); } catch (utimeser) { er = futimeser; } @@ -17070,10 +16673,10 @@ var init_unpack = __esm({ const uid = this[UID](entry); const gid = this[GID](entry); try { - import_node_fs3.default.fchownSync(fd, Number(uid), Number(gid)); + import_node_fs4.default.fchownSync(fd, Number(uid), Number(gid)); } catch (fchowner) { try { - import_node_fs3.default.chownSync(String(entry.absolute), Number(uid), Number(gid)); + import_node_fs4.default.chownSync(String(entry.absolute), Number(uid), Number(gid)); } catch (chowner) { er = er || fchowner; } @@ -17092,13 +16695,13 @@ var init_unpack = __esm({ } if (entry.mtime && !this.noMtime) { try { - import_node_fs3.default.utimesSync(String(entry.absolute), entry.atime || /* @__PURE__ */ new Date(), entry.mtime); + import_node_fs4.default.utimesSync(String(entry.absolute), entry.atime || /* @__PURE__ */ new Date(), entry.mtime); } catch (er2) { } } if (this[DOCHOWN](entry)) { try { - import_node_fs3.default.chownSync(String(entry.absolute), Number(this[UID](entry)), Number(this[GID](entry))); + import_node_fs4.default.chownSync(String(entry.absolute), Number(this[UID](entry)), Number(this[GID](entry))); } catch (er2) { } } @@ -17107,7 +16710,7 @@ var init_unpack = __esm({ } [MKDIR](dir, mode) { try { - return mkdirSync4(normalizeWindowsPath(dir), { + return mkdirSync2(normalizeWindowsPath(dir), { uid: this.uid, gid: this.gid, processUid: this.processUid, @@ -17115,7 +16718,6 @@ var init_unpack = __esm({ umask: this.processUmask, preserve: this.preservePaths, unlink: this.unlink, - cache: this.dirCache, cwd: this.cwd, mode }); @@ -17126,7 +16728,7 @@ var init_unpack = __esm({ [LINK](entry, linkpath, link, done) { const ls = `${link}Sync`; try { - import_node_fs3.default[ls](linkpath, String(entry.absolute)); + import_node_fs4.default[ls](linkpath, String(entry.absolute)); done(); entry.resume(); } catch (er) { @@ -17137,27 +16739,27 @@ var init_unpack = __esm({ } }); -// .yarn/cache/tar-npm-7.4.3-1dbbd1ffc3-d4679609bb.zip/node_modules/tar/dist/esm/extract.js +// .yarn/cache/tar-npm-7.5.1-7b414f7fec-0dad0596a6.zip/node_modules/tar/dist/esm/extract.js var extract_exports = {}; __export(extract_exports, { extract: () => extract }); -var import_node_fs4, extractFileSync, extractFile, extract; +var import_node_fs5, extractFileSync, extractFile, extract; var init_extract = __esm({ - ".yarn/cache/tar-npm-7.4.3-1dbbd1ffc3-d4679609bb.zip/node_modules/tar/dist/esm/extract.js"() { + ".yarn/cache/tar-npm-7.5.1-7b414f7fec-0dad0596a6.zip/node_modules/tar/dist/esm/extract.js"() { init_esm2(); - import_node_fs4 = __toESM(require("node:fs"), 1); + import_node_fs5 = __toESM(require("node:fs"), 1); init_list(); init_make_command(); init_unpack(); extractFileSync = (opt) => { const u = new UnpackSync(opt); const file = opt.file; - const stat2 = import_node_fs4.default.statSync(file); + const stat = import_node_fs5.default.statSync(file); const readSize = opt.maxReadSize || 16 * 1024 * 1024; const stream = new ReadStreamSync(file, { readSize, - size: stat2.size + size: stat.size }); stream.pipe(u); }; @@ -17165,16 +16767,16 @@ var init_extract = __esm({ const u = new Unpack(opt); const readSize = opt.maxReadSize || 16 * 1024 * 1024; const file = opt.file; - const p = new Promise((resolve2, reject) => { + const p = new Promise((resolve, reject) => { u.on("error", reject); - u.on("close", resolve2); - import_node_fs4.default.stat(file, (er, stat2) => { + u.on("close", resolve); + import_node_fs5.default.stat(file, (er, stat) => { if (er) { reject(er); } else { const stream = new ReadStream(file, { readSize, - size: stat2.size + size: stat.size }); stream.on("error", reject); stream.pipe(u); @@ -17200,7 +16802,7 @@ var require_v8_compile_cache = __commonJS({ var path16 = require("path"); var vm = require("vm"); var os3 = require("os"); - var hasOwnProperty2 = Object.prototype.hasOwnProperty; + var hasOwnProperty = Object.prototype.hasOwnProperty; var FileSystemBlobStore = class { constructor(directory, prefix) { const name2 = prefix ? slashEscape(prefix + ".") : ""; @@ -17211,19 +16813,19 @@ var require_v8_compile_cache = __commonJS({ this._load(); } has(key, invalidationKey) { - if (hasOwnProperty2.call(this._memoryBlobs, key)) { + if (hasOwnProperty.call(this._memoryBlobs, key)) { return this._invalidationKeys[key] === invalidationKey; - } else if (hasOwnProperty2.call(this._storedMap, key)) { + } else if (hasOwnProperty.call(this._storedMap, key)) { return this._storedMap[key][0] === invalidationKey; } return false; } get(key, invalidationKey) { - if (hasOwnProperty2.call(this._memoryBlobs, key)) { + if (hasOwnProperty.call(this._memoryBlobs, key)) { if (this._invalidationKeys[key] === invalidationKey) { return this._memoryBlobs[key]; } - } else if (hasOwnProperty2.call(this._storedMap, key)) { + } else if (hasOwnProperty.call(this._storedMap, key)) { const mapping = this._storedMap[key]; if (mapping[0] === invalidationKey) { return this._storedBlob.slice(mapping[1], mapping[2]); @@ -17236,15 +16838,15 @@ var require_v8_compile_cache = __commonJS({ this._dirty = true; } delete(key) { - if (hasOwnProperty2.call(this._memoryBlobs, key)) { + if (hasOwnProperty.call(this._memoryBlobs, key)) { this._dirty = true; delete this._memoryBlobs[key]; } - if (hasOwnProperty2.call(this._invalidationKeys, key)) { + if (hasOwnProperty.call(this._invalidationKeys, key)) { this._dirty = true; delete this._invalidationKeys[key]; } - if (hasOwnProperty2.call(this._storedMap, key)) { + if (hasOwnProperty.call(this._storedMap, key)) { this._dirty = true; delete this._storedMap[key]; } @@ -17257,7 +16859,7 @@ var require_v8_compile_cache = __commonJS({ const blobToStore = Buffer.concat(dump[0]); const mapToStore = JSON.stringify(dump[1]); try { - mkdirpSync2(this._directory); + mkdirpSync(this._directory); fs17.writeFileSync(this._lockFilename, "LOCK", { flag: "wx" }); } catch (error) { return false; @@ -17297,7 +16899,7 @@ var require_v8_compile_cache = __commonJS({ push2(key, invalidationKey, buffer); } for (const key of Object.keys(this._storedMap)) { - if (hasOwnProperty2.call(newMap, key)) continue; + if (hasOwnProperty.call(newMap, key)) continue; const mapping = this._storedMap[key]; const buffer = this._storedBlob.slice(mapping[1], mapping[2]); push2(key, mapping[0], buffer); @@ -17322,21 +16924,21 @@ var require_v8_compile_cache = __commonJS({ function require2(id) { return mod.require(id); } - function resolve2(request, options) { + function resolve(request, options) { return Module2._resolveFilename(request, mod, false, options); } - require2.resolve = resolve2; + require2.resolve = resolve; if (hasRequireResolvePaths) { - resolve2.paths = function paths(request) { + resolve.paths = function paths(request) { return Module2._resolveLookupPaths(request, mod, true); }; } require2.main = process.mainModule; require2.extensions = Module2._extensions; require2.cache = Module2._cache; - const dirname5 = path16.dirname(filename); + const dirname2 = path16.dirname(filename); const compiledWrapper = self2._moduleCompile(filename, content); - const args = [mod.exports, require2, mod, filename, dirname5, process, global, Buffer]; + const args = [mod.exports, require2, mod, filename, dirname2, process, global, Buffer]; return compiledWrapper.apply(mod.exports, args); }; } @@ -17387,7 +16989,7 @@ var require_v8_compile_cache = __commonJS({ return compiledWrapper; } }; - function mkdirpSync2(p_) { + function mkdirpSync(p_) { _mkdirpSync(path16.resolve(p_), 511); } function _mkdirpSync(p, mode) { @@ -17399,8 +17001,8 @@ var require_v8_compile_cache = __commonJS({ _mkdirpSync(p); } else { try { - const stat2 = fs17.statSync(p); - if (!stat2.isDirectory()) { + const stat = fs17.statSync(p); + if (!stat.isDirectory()) { throw err0; } } catch (err1) { @@ -17429,10 +17031,10 @@ var require_v8_compile_cache = __commonJS({ if (v8_compile_cache_cache_dir) { return v8_compile_cache_cache_dir; } - const dirname5 = typeof process.getuid === "function" ? "v8-compile-cache-" + process.getuid() : "v8-compile-cache"; + const dirname2 = typeof process.getuid === "function" ? "v8-compile-cache-" + process.getuid() : "v8-compile-cache"; const arch = process.arch; - const version3 = typeof process.versions.v8 === "string" ? process.versions.v8 : typeof process.versions.chakracore === "string" ? "chakracore-" + process.versions.chakracore : "node-" + process.version; - const cacheDir = path16.join(os3.tmpdir(), dirname5, arch, version3); + const version2 = typeof process.versions.v8 === "string" ? process.versions.v8 : typeof process.versions.chakracore === "string" ? "chakracore-" + process.versions.chakracore : "node-" + process.version; + const cacheDir = path16.join(os3.tmpdir(), dirname2, arch, version2); return cacheDir; } function getMainName() { @@ -17456,7 +17058,7 @@ var require_v8_compile_cache = __commonJS({ module2.exports.__TEST__ = { FileSystemBlobStore, NativeCompileCache, - mkdirpSync: mkdirpSync2, + mkdirpSync, slashEscape, supportsCachedData, getCacheDir, @@ -17465,17 +17067,18 @@ var require_v8_compile_cache = __commonJS({ } }); -// .yarn/cache/semver-npm-7.7.1-4572475307-fd603a6fb9.zip/node_modules/semver/functions/satisfies.js +// .yarn/cache/semver-npm-7.7.3-9cf7b3b46c-4afe5c9865.zip/node_modules/semver/functions/satisfies.js var require_satisfies = __commonJS({ - ".yarn/cache/semver-npm-7.7.1-4572475307-fd603a6fb9.zip/node_modules/semver/functions/satisfies.js"(exports2, module2) { + ".yarn/cache/semver-npm-7.7.3-9cf7b3b46c-4afe5c9865.zip/node_modules/semver/functions/satisfies.js"(exports2, module2) { + "use strict"; var Range3 = require_range(); - var satisfies = (version3, range, options) => { + var satisfies = (version2, range, options) => { try { range = new Range3(range, options); } catch (er) { return false; } - return range.test(version3); + return range.test(version2); }; module2.exports = satisfies; } @@ -17513,8 +17116,8 @@ var require_posix = __commonJS({ } }; exports2.sync = sync; - var checkStat = (stat2, options) => stat2.isFile() && checkMode(stat2, options); - var checkMode = (stat2, options) => { + var checkStat = (stat, options) => stat.isFile() && checkMode(stat, options); + var checkMode = (stat, options) => { const myUid = options.uid ?? process.getuid?.(); const myGroups = options.groups ?? process.getgroups?.() ?? []; const myGid = options.gid ?? process.getgid?.() ?? myGroups[0]; @@ -17522,9 +17125,9 @@ var require_posix = __commonJS({ throw new Error("cannot get uid or gid"); } const groups = /* @__PURE__ */ new Set([myGid, ...myGroups]); - const mod = stat2.mode; - const uid = stat2.uid; - const gid = stat2.gid; + const mod = stat.mode; + const uid = stat.uid; + const gid = stat.gid; const u = parseInt("100", 8); const g = parseInt("010", 8); const o = parseInt("001", 8); @@ -17581,7 +17184,7 @@ var require_win32 = __commonJS({ } return false; }; - var checkStat = (stat2, path16, options) => stat2.isFile() && checkPathExt(path16, options); + var checkStat = (stat, path16, options) => stat.isFile() && checkPathExt(path16, options); } }); @@ -17597,22 +17200,22 @@ var require_options = __commonJS({ var require_cjs = __commonJS({ ".yarn/cache/isexe-npm-3.1.1-9c0061eead-9ec2576540.zip/node_modules/isexe/dist/cjs/index.js"(exports2) { "use strict"; - var __createBinding = exports2 && exports2.__createBinding || (Object.create ? function(o, m, k, k2) { + var __createBinding = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) { if (k2 === void 0) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { + var desc2 = Object.getOwnPropertyDescriptor(m, k); + if (!desc2 || ("get" in desc2 ? !m.__esModule : desc2.writable || desc2.configurable)) { + desc2 = { enumerable: true, get: function() { return m[k]; } }; } - Object.defineProperty(o, k2, desc); - } : function(o, m, k, k2) { + Object.defineProperty(o, k2, desc2); + }) : (function(o, m, k, k2) { if (k2 === void 0) k2 = k; o[k2] = m[k]; - }); - var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? function(o, v) { + })); + var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); - } : function(o, v) { + }) : function(o, v) { o["default"] = v; }); var __importStar = exports2 && exports2.__importStar || function(mod) { @@ -17634,8 +17237,8 @@ var require_cjs = __commonJS({ var win322 = __importStar(require_win32()); exports2.win32 = win322; __exportStar(require_options(), exports2); - var platform6 = process.env._ISEXE_TEST_PLATFORM_ || process.platform; - var impl = platform6 === "win32" ? win322 : posix; + var platform5 = process.env._ISEXE_TEST_PLATFORM_ || process.platform; + var impl = platform5 === "win32" ? win322 : posix; exports2.isexe = impl.isexe; exports2.sync = impl.sync; } @@ -17774,7 +17377,7 @@ var require_polyfills = __commonJS({ var constants2 = require("constants"); var origCwd = process.cwd; var cwd = null; - var platform6 = process.env.GRACEFUL_FS_PLATFORM || process.platform; + var platform5 = process.env.GRACEFUL_FS_PLATFORM || process.platform; process.cwd = function() { if (!cwd) cwd = origCwd.call(process); @@ -17833,8 +17436,8 @@ var require_polyfills = __commonJS({ fs17.lchownSync = function() { }; } - if (platform6 === "win32") { - fs17.rename = typeof fs17.rename !== "function" ? fs17.rename : function(fs$rename) { + if (platform5 === "win32") { + fs17.rename = typeof fs17.rename !== "function" ? fs17.rename : (function(fs$rename) { function rename(from, to, cb) { var start = Date.now(); var backoff = 0; @@ -17857,9 +17460,9 @@ var require_polyfills = __commonJS({ } if (Object.setPrototypeOf) Object.setPrototypeOf(rename, fs$rename); return rename; - }(fs17.rename); + })(fs17.rename); } - fs17.read = typeof fs17.read !== "function" ? fs17.read : function(fs$read) { + fs17.read = typeof fs17.read !== "function" ? fs17.read : (function(fs$read) { function read(fd, buffer, offset, length, position, callback_) { var callback; if (callback_ && typeof callback_ === "function") { @@ -17876,8 +17479,8 @@ var require_polyfills = __commonJS({ } if (Object.setPrototypeOf) Object.setPrototypeOf(read, fs$read); return read; - }(fs17.read); - fs17.readSync = typeof fs17.readSync !== "function" ? fs17.readSync : /* @__PURE__ */ function(fs$readSync) { + })(fs17.read); + fs17.readSync = typeof fs17.readSync !== "function" ? fs17.readSync : /* @__PURE__ */ (function(fs$readSync) { return function(fd, buffer, offset, length, position) { var eagCounter = 0; while (true) { @@ -17892,7 +17495,7 @@ var require_polyfills = __commonJS({ } } }; - }(fs17.readSync); + })(fs17.readSync); function patchLchmod(fs18) { fs18.lchmod = function(path16, mode, callback) { fs18.open( @@ -18192,7 +17795,7 @@ var require_graceful_fs = __commonJS({ gracefulQueue = "___graceful-fs.queue"; previousSymbol = "___graceful-fs.previous"; } - function noop2() { + function noop3() { } function publishQueue(context, queue2) { Object.defineProperty(context, gracefulQueue, { @@ -18201,7 +17804,7 @@ var require_graceful_fs = __commonJS({ } }); } - var debug2 = noop2; + var debug2 = noop3; if (util.debuglog) debug2 = util.debuglog("gfs4"); else if (/\bgfs4\b/i.test(process.env.NODE_DEBUG || "")) @@ -18213,7 +17816,7 @@ var require_graceful_fs = __commonJS({ if (!fs17[gracefulQueue]) { queue = global[gracefulQueue] || []; publishQueue(fs17, queue); - fs17.close = function(fs$close) { + fs17.close = (function(fs$close) { function close(fd, cb) { return fs$close.call(fs17, fd, function(err) { if (!err) { @@ -18227,8 +17830,8 @@ var require_graceful_fs = __commonJS({ value: fs$close }); return close; - }(fs17.close); - fs17.closeSync = function(fs$closeSync) { + })(fs17.close); + fs17.closeSync = (function(fs$closeSync) { function closeSync(fd) { fs$closeSync.apply(fs17, arguments); resetQueue(); @@ -18237,7 +17840,7 @@ var require_graceful_fs = __commonJS({ value: fs$closeSync }); return closeSync; - }(fs17.closeSync); + })(fs17.closeSync); if (/\bgfs4\b/i.test(process.env.NODE_DEBUG || "")) { process.on("exit", function() { debug2(fs17[gracefulQueue]); @@ -18455,1072 +18058,1446 @@ var require_graceful_fs = __commonJS({ else return WriteStream2.apply(Object.create(WriteStream2.prototype), arguments); } - function WriteStream$open() { - var that = this; - open(that.path, that.flags, that.mode, function(err, fd) { - if (err) { - that.destroy(); - that.emit("error", err); - } else { - that.fd = fd; - that.emit("open", fd); - } - }); + function WriteStream$open() { + var that = this; + open(that.path, that.flags, that.mode, function(err, fd) { + if (err) { + that.destroy(); + that.emit("error", err); + } else { + that.fd = fd; + that.emit("open", fd); + } + }); + } + function createReadStream(path16, options) { + return new fs18.ReadStream(path16, options); + } + function createWriteStream(path16, options) { + return new fs18.WriteStream(path16, options); + } + var fs$open = fs18.open; + fs18.open = open; + function open(path16, flags, mode, cb) { + if (typeof mode === "function") + cb = mode, mode = null; + return go$open(path16, flags, mode, cb); + function go$open(path17, flags2, mode2, cb2, startTime) { + return fs$open(path17, flags2, mode2, function(err, fd) { + if (err && (err.code === "EMFILE" || err.code === "ENFILE")) + enqueue([go$open, [path17, flags2, mode2, cb2], err, startTime || Date.now(), Date.now()]); + else { + if (typeof cb2 === "function") + cb2.apply(this, arguments); + } + }); + } + } + return fs18; + } + function enqueue(elem) { + debug2("ENQUEUE", elem[0].name, elem[1]); + fs17[gracefulQueue].push(elem); + retry(); + } + var retryTimer; + function resetQueue() { + var now = Date.now(); + for (var i = 0; i < fs17[gracefulQueue].length; ++i) { + if (fs17[gracefulQueue][i].length > 2) { + fs17[gracefulQueue][i][3] = now; + fs17[gracefulQueue][i][4] = now; + } + } + retry(); + } + function retry() { + clearTimeout(retryTimer); + retryTimer = void 0; + if (fs17[gracefulQueue].length === 0) + return; + var elem = fs17[gracefulQueue].shift(); + var fn2 = elem[0]; + var args = elem[1]; + var err = elem[2]; + var startTime = elem[3]; + var lastTime = elem[4]; + if (startTime === void 0) { + debug2("RETRY", fn2.name, args); + fn2.apply(null, args); + } else if (Date.now() - startTime >= 6e4) { + debug2("TIMEOUT", fn2.name, args); + var cb = args.pop(); + if (typeof cb === "function") + cb.call(null, err); + } else { + var sinceAttempt = Date.now() - lastTime; + var sinceStart = Math.max(lastTime - startTime, 1); + var desiredDelay = Math.min(sinceStart * 1.2, 100); + if (sinceAttempt >= desiredDelay) { + debug2("RETRY", fn2.name, args); + fn2.apply(null, args.concat([startTime])); + } else { + fs17[gracefulQueue].push(elem); + } + } + if (retryTimer === void 0) { + retryTimer = setTimeout(retry, 0); + } + } + } +}); + +// .yarn/cache/@zkochan-cmd-shim-npm-6.0.0-97792a7373-ba1442ba1e.zip/node_modules/@zkochan/cmd-shim/index.js +var require_cmd_shim = __commonJS({ + ".yarn/cache/@zkochan-cmd-shim-npm-6.0.0-97792a7373-ba1442ba1e.zip/node_modules/@zkochan/cmd-shim/index.js"(exports2, module2) { + "use strict"; + cmdShim2.ifExists = cmdShimIfExists; + var util_1 = require("util"); + var path16 = require("path"); + var isWindows4 = require_is_windows(); + var CMD_EXTENSION = require_cmd_extension(); + var shebangExpr = /^#!\s*(?:\/usr\/bin\/env(?:\s+-S\s*)?)?\s*([^ \t]+)(.*)$/; + var DEFAULT_OPTIONS = { + // Create PowerShell file by default if the option hasn't been specified + createPwshFile: true, + createCmdFile: isWindows4(), + fs: require_graceful_fs() + }; + var extensionToProgramMap = /* @__PURE__ */ new Map([ + [".js", "node"], + [".cjs", "node"], + [".mjs", "node"], + [".cmd", "cmd"], + [".bat", "cmd"], + [".ps1", "pwsh"], + [".sh", "sh"] + ]); + function ingestOptions(opts) { + const opts_ = { ...DEFAULT_OPTIONS, ...opts }; + const fs17 = opts_.fs; + opts_.fs_ = { + chmod: fs17.chmod ? (0, util_1.promisify)(fs17.chmod) : (async () => { + }), + mkdir: (0, util_1.promisify)(fs17.mkdir), + readFile: (0, util_1.promisify)(fs17.readFile), + stat: (0, util_1.promisify)(fs17.stat), + unlink: (0, util_1.promisify)(fs17.unlink), + writeFile: (0, util_1.promisify)(fs17.writeFile) + }; + return opts_; + } + async function cmdShim2(src, to, opts) { + const opts_ = ingestOptions(opts); + await cmdShim_(src, to, opts_); + } + function cmdShimIfExists(src, to, opts) { + return cmdShim2(src, to, opts).catch(() => { + }); + } + function rm(path17, opts) { + return opts.fs_.unlink(path17).catch(() => { + }); + } + async function cmdShim_(src, to, opts) { + const srcRuntimeInfo = await searchScriptRuntime(src, opts); + await writeShimsPreCommon(to, opts); + return writeAllShims(src, to, srcRuntimeInfo, opts); + } + function writeShimsPreCommon(target, opts) { + return opts.fs_.mkdir(path16.dirname(target), { recursive: true }); + } + function writeAllShims(src, to, srcRuntimeInfo, opts) { + const opts_ = ingestOptions(opts); + const generatorAndExts = [{ generator: generateShShim, extension: "" }]; + if (opts_.createCmdFile) { + generatorAndExts.push({ generator: generateCmdShim, extension: CMD_EXTENSION }); + } + if (opts_.createPwshFile) { + generatorAndExts.push({ generator: generatePwshShim, extension: ".ps1" }); + } + return Promise.all(generatorAndExts.map((generatorAndExt) => writeShim(src, to + generatorAndExt.extension, srcRuntimeInfo, generatorAndExt.generator, opts_))); + } + function writeShimPre(target, opts) { + return rm(target, opts); + } + function writeShimPost(target, opts) { + return chmodShim(target, opts); + } + async function searchScriptRuntime(target, opts) { + try { + const data = await opts.fs_.readFile(target, "utf8"); + const firstLine = data.trim().split(/\r*\n/)[0]; + const shebang = firstLine.match(shebangExpr); + if (!shebang) { + const targetExtension = path16.extname(target).toLowerCase(); + return { + // undefined if extension is unknown but it's converted to null. + program: extensionToProgramMap.get(targetExtension) || null, + additionalArgs: "" + }; + } + return { + program: shebang[1], + additionalArgs: shebang[2] + }; + } catch (err) { + if (!isWindows4() || err.code !== "ENOENT") + throw err; + if (await opts.fs_.stat(`${target}${getExeExtension()}`)) { + return { + program: null, + additionalArgs: "" + }; + } + throw err; + } + } + function getExeExtension() { + let cmdExtension; + if (process.env.PATHEXT) { + cmdExtension = process.env.PATHEXT.split(path16.delimiter).find((ext) => ext.toLowerCase() === ".exe"); + } + return cmdExtension || ".exe"; + } + async function writeShim(src, to, srcRuntimeInfo, generateShimScript, opts) { + const defaultArgs = opts.preserveSymlinks ? "--preserve-symlinks" : ""; + const args = [srcRuntimeInfo.additionalArgs, defaultArgs].filter((arg) => arg).join(" "); + opts = Object.assign({}, opts, { + prog: srcRuntimeInfo.program, + args + }); + await writeShimPre(to, opts); + await opts.fs_.writeFile(to, generateShimScript(src, to, opts), "utf8"); + return writeShimPost(to, opts); + } + function generateCmdShim(src, to, opts) { + const shTarget = path16.relative(path16.dirname(to), src); + let target = shTarget.split("/").join("\\"); + const quotedPathToTarget = path16.isAbsolute(target) ? `"${target}"` : `"%~dp0\\${target}"`; + let longProg; + let prog = opts.prog; + let args = opts.args || ""; + const nodePath = normalizePathEnvVar(opts.nodePath).win32; + const prependToPath = normalizePathEnvVar(opts.prependToPath).win32; + if (!prog) { + prog = quotedPathToTarget; + args = ""; + target = ""; + } else if (prog === "node" && opts.nodeExecPath) { + prog = `"${opts.nodeExecPath}"`; + target = quotedPathToTarget; + } else { + longProg = `"%~dp0\\${prog}.exe"`; + target = quotedPathToTarget; + } + let progArgs = opts.progArgs ? `${opts.progArgs.join(` `)} ` : ""; + let cmd = "@SETLOCAL\r\n"; + if (prependToPath) { + cmd += `@SET "PATH=${prependToPath}:%PATH%"\r +`; + } + if (nodePath) { + cmd += `@IF NOT DEFINED NODE_PATH (\r + @SET "NODE_PATH=${nodePath}"\r +) ELSE (\r + @SET "NODE_PATH=${nodePath};%NODE_PATH%"\r +)\r +`; + } + if (longProg) { + cmd += `@IF EXIST ${longProg} (\r + ${longProg} ${args} ${target} ${progArgs}%*\r +) ELSE (\r + @SET PATHEXT=%PATHEXT:;.JS;=;%\r + ${prog} ${args} ${target} ${progArgs}%*\r +)\r +`; + } else { + cmd += `@${prog} ${args} ${target} ${progArgs}%*\r +`; + } + return cmd; + } + function generateShShim(src, to, opts) { + let shTarget = path16.relative(path16.dirname(to), src); + let shProg = opts.prog && opts.prog.split("\\").join("/"); + let shLongProg; + shTarget = shTarget.split("\\").join("/"); + const quotedPathToTarget = path16.isAbsolute(shTarget) ? `"${shTarget}"` : `"$basedir/${shTarget}"`; + let args = opts.args || ""; + const shNodePath = normalizePathEnvVar(opts.nodePath).posix; + if (!shProg) { + shProg = quotedPathToTarget; + args = ""; + shTarget = ""; + } else if (opts.prog === "node" && opts.nodeExecPath) { + shProg = `"${opts.nodeExecPath}"`; + shTarget = quotedPathToTarget; + } else { + shLongProg = `"$basedir/${opts.prog}"`; + shTarget = quotedPathToTarget; + } + let progArgs = opts.progArgs ? `${opts.progArgs.join(` `)} ` : ""; + let sh = `#!/bin/sh +basedir=$(dirname "$(echo "$0" | sed -e 's,\\\\,/,g')") + +case \`uname\` in + *CYGWIN*) basedir=\`cygpath -w "$basedir"\`;; +esac + +`; + if (opts.prependToPath) { + sh += `export PATH="${opts.prependToPath}:$PATH" +`; + } + if (shNodePath) { + sh += `if [ -z "$NODE_PATH" ]; then + export NODE_PATH="${shNodePath}" +else + export NODE_PATH="${shNodePath}:$NODE_PATH" +fi +`; + } + if (shLongProg) { + sh += `if [ -x ${shLongProg} ]; then + exec ${shLongProg} ${args} ${shTarget} ${progArgs}"$@" +else + exec ${shProg} ${args} ${shTarget} ${progArgs}"$@" +fi +`; + } else { + sh += `${shProg} ${args} ${shTarget} ${progArgs}"$@" +exit $? +`; + } + return sh; + } + function generatePwshShim(src, to, opts) { + let shTarget = path16.relative(path16.dirname(to), src); + const shProg = opts.prog && opts.prog.split("\\").join("/"); + let pwshProg = shProg && `"${shProg}$exe"`; + let pwshLongProg; + shTarget = shTarget.split("\\").join("/"); + const quotedPathToTarget = path16.isAbsolute(shTarget) ? `"${shTarget}"` : `"$basedir/${shTarget}"`; + let args = opts.args || ""; + let normalizedNodePathEnvVar = normalizePathEnvVar(opts.nodePath); + const nodePath = normalizedNodePathEnvVar.win32; + const shNodePath = normalizedNodePathEnvVar.posix; + let normalizedPrependPathEnvVar = normalizePathEnvVar(opts.prependToPath); + const prependPath = normalizedPrependPathEnvVar.win32; + const shPrependPath = normalizedPrependPathEnvVar.posix; + if (!pwshProg) { + pwshProg = quotedPathToTarget; + args = ""; + shTarget = ""; + } else if (opts.prog === "node" && opts.nodeExecPath) { + pwshProg = `"${opts.nodeExecPath}"`; + shTarget = quotedPathToTarget; + } else { + pwshLongProg = `"$basedir/${opts.prog}$exe"`; + shTarget = quotedPathToTarget; + } + let progArgs = opts.progArgs ? `${opts.progArgs.join(` `)} ` : ""; + let pwsh = `#!/usr/bin/env pwsh +$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent + +$exe="" +${nodePath || prependPath ? '$pathsep=":"\n' : ""}${nodePath ? `$env_node_path=$env:NODE_PATH +$new_node_path="${nodePath}" +` : ""}${prependPath ? `$env_path=$env:PATH +$prepend_path="${prependPath}" +` : ""}if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) { + # Fix case when both the Windows and Linux builds of Node + # are installed in the same directory + $exe=".exe" +${nodePath || prependPath ? ' $pathsep=";"\n' : ""}}`; + if (shNodePath || shPrependPath) { + pwsh += ` else { +${shNodePath ? ` $new_node_path="${shNodePath}" +` : ""}${shPrependPath ? ` $prepend_path="${shPrependPath}" +` : ""}} +`; + } + if (shNodePath) { + pwsh += `if ([string]::IsNullOrEmpty($env_node_path)) { + $env:NODE_PATH=$new_node_path +} else { + $env:NODE_PATH="$new_node_path$pathsep$env_node_path" +} +`; + } + if (opts.prependToPath) { + pwsh += ` +$env:PATH="$prepend_path$pathsep$env:PATH" +`; + } + if (pwshLongProg) { + pwsh += ` +$ret=0 +if (Test-Path ${pwshLongProg}) { + # Support pipeline input + if ($MyInvocation.ExpectingInput) { + $input | & ${pwshLongProg} ${args} ${shTarget} ${progArgs}$args + } else { + & ${pwshLongProg} ${args} ${shTarget} ${progArgs}$args + } + $ret=$LASTEXITCODE +} else { + # Support pipeline input + if ($MyInvocation.ExpectingInput) { + $input | & ${pwshProg} ${args} ${shTarget} ${progArgs}$args + } else { + & ${pwshProg} ${args} ${shTarget} ${progArgs}$args + } + $ret=$LASTEXITCODE +} +${nodePath ? "$env:NODE_PATH=$env_node_path\n" : ""}${prependPath ? "$env:PATH=$env_path\n" : ""}exit $ret +`; + } else { + pwsh += ` +# Support pipeline input +if ($MyInvocation.ExpectingInput) { + $input | & ${pwshProg} ${args} ${shTarget} ${progArgs}$args +} else { + & ${pwshProg} ${args} ${shTarget} ${progArgs}$args +} +${nodePath ? "$env:NODE_PATH=$env_node_path\n" : ""}${prependPath ? "$env:PATH=$env_path\n" : ""}exit $LASTEXITCODE +`; + } + return pwsh; + } + function chmodShim(to, opts) { + return opts.fs_.chmod(to, 493); + } + function normalizePathEnvVar(nodePath) { + if (!nodePath || !nodePath.length) { + return { + win32: "", + posix: "" + }; } - function createReadStream(path16, options) { - return new fs18.ReadStream(path16, options); + let split = typeof nodePath === "string" ? nodePath.split(path16.delimiter) : Array.from(nodePath); + let result = {}; + for (let i = 0; i < split.length; i++) { + const win322 = split[i].split("/").join("\\"); + const posix = isWindows4() ? split[i].split("\\").join("/").replace(/^([^:\\/]*):/, (_, $1) => `/mnt/${$1.toLowerCase()}`) : split[i]; + result.win32 = result.win32 ? `${result.win32};${win322}` : win322; + result.posix = result.posix ? `${result.posix}:${posix}` : posix; + result[i] = { win32: win322, posix }; } - function createWriteStream(path16, options) { - return new fs18.WriteStream(path16, options); + return result; + } + module2.exports = cmdShim2; + } +}); + +// .yarn/cache/tar-npm-7.5.1-7b414f7fec-0dad0596a6.zip/node_modules/tar/dist/esm/mode-fix.js +var modeFix; +var init_mode_fix = __esm({ + ".yarn/cache/tar-npm-7.5.1-7b414f7fec-0dad0596a6.zip/node_modules/tar/dist/esm/mode-fix.js"() { + modeFix = (mode, isDir, portable) => { + mode &= 4095; + if (portable) { + mode = (mode | 384) & ~18; } - var fs$open = fs18.open; - fs18.open = open; - function open(path16, flags, mode, cb) { - if (typeof mode === "function") - cb = mode, mode = null; - return go$open(path16, flags, mode, cb); - function go$open(path17, flags2, mode2, cb2, startTime) { - return fs$open(path17, flags2, mode2, function(err, fd) { - if (err && (err.code === "EMFILE" || err.code === "ENFILE")) - enqueue([go$open, [path17, flags2, mode2, cb2], err, startTime || Date.now(), Date.now()]); - else { - if (typeof cb2 === "function") - cb2.apply(this, arguments); - } - }); + if (isDir) { + if (mode & 256) { + mode |= 64; } - } - return fs18; - } - function enqueue(elem) { - debug2("ENQUEUE", elem[0].name, elem[1]); - fs17[gracefulQueue].push(elem); - retry(); - } - var retryTimer; - function resetQueue() { - var now = Date.now(); - for (var i = 0; i < fs17[gracefulQueue].length; ++i) { - if (fs17[gracefulQueue][i].length > 2) { - fs17[gracefulQueue][i][3] = now; - fs17[gracefulQueue][i][4] = now; + if (mode & 32) { + mode |= 8; } - } - retry(); - } - function retry() { - clearTimeout(retryTimer); - retryTimer = void 0; - if (fs17[gracefulQueue].length === 0) - return; - var elem = fs17[gracefulQueue].shift(); - var fn2 = elem[0]; - var args = elem[1]; - var err = elem[2]; - var startTime = elem[3]; - var lastTime = elem[4]; - if (startTime === void 0) { - debug2("RETRY", fn2.name, args); - fn2.apply(null, args); - } else if (Date.now() - startTime >= 6e4) { - debug2("TIMEOUT", fn2.name, args); - var cb = args.pop(); - if (typeof cb === "function") - cb.call(null, err); - } else { - var sinceAttempt = Date.now() - lastTime; - var sinceStart = Math.max(lastTime - startTime, 1); - var desiredDelay = Math.min(sinceStart * 1.2, 100); - if (sinceAttempt >= desiredDelay) { - debug2("RETRY", fn2.name, args); - fn2.apply(null, args.concat([startTime])); - } else { - fs17[gracefulQueue].push(elem); + if (mode & 4) { + mode |= 1; } } - if (retryTimer === void 0) { - retryTimer = setTimeout(retry, 0); - } - } + return mode; + }; } }); -// .yarn/cache/@zkochan-cmd-shim-npm-6.0.0-97792a7373-ba1442ba1e.zip/node_modules/@zkochan/cmd-shim/index.js -var require_cmd_shim = __commonJS({ - ".yarn/cache/@zkochan-cmd-shim-npm-6.0.0-97792a7373-ba1442ba1e.zip/node_modules/@zkochan/cmd-shim/index.js"(exports2, module2) { - "use strict"; - cmdShim2.ifExists = cmdShimIfExists; - var util_1 = require("util"); - var path16 = require("path"); - var isWindows4 = require_is_windows(); - var CMD_EXTENSION = require_cmd_extension(); - var shebangExpr = /^#!\s*(?:\/usr\/bin\/env(?:\s+-S\s*)?)?\s*([^ \t]+)(.*)$/; - var DEFAULT_OPTIONS = { - // Create PowerShell file by default if the option hasn't been specified - createPwshFile: true, - createCmdFile: isWindows4(), - fs: require_graceful_fs() +// .yarn/cache/tar-npm-7.5.1-7b414f7fec-0dad0596a6.zip/node_modules/tar/dist/esm/write-entry.js +var import_fs11, import_path9, prefixPath, maxReadSize, PROCESS, FILE2, DIRECTORY2, SYMLINK2, HARDLINK2, HEADER, READ2, LSTAT, ONLSTAT, ONREAD, ONREADLINK, OPENFILE, ONOPENFILE, CLOSE, MODE, AWAITDRAIN, ONDRAIN, PREFIX, WriteEntry, WriteEntrySync, WriteEntryTar, getType; +var init_write_entry = __esm({ + ".yarn/cache/tar-npm-7.5.1-7b414f7fec-0dad0596a6.zip/node_modules/tar/dist/esm/write-entry.js"() { + import_fs11 = __toESM(require("fs"), 1); + init_esm(); + import_path9 = __toESM(require("path"), 1); + init_header(); + init_mode_fix(); + init_normalize_windows_path(); + init_options(); + init_pax(); + init_strip_absolute_path(); + init_strip_trailing_slashes(); + init_warn_method(); + init_winchars(); + prefixPath = (path16, prefix) => { + if (!prefix) { + return normalizeWindowsPath(path16); + } + path16 = normalizeWindowsPath(path16).replace(/^\.(\/|$)/, ""); + return stripTrailingSlashes(prefix) + "/" + path16; }; - var extensionToProgramMap = /* @__PURE__ */ new Map([ - [".js", "node"], - [".cjs", "node"], - [".mjs", "node"], - [".cmd", "cmd"], - [".bat", "cmd"], - [".ps1", "pwsh"], - [".sh", "sh"] - ]); - function ingestOptions(opts) { - const opts_ = { ...DEFAULT_OPTIONS, ...opts }; - const fs17 = opts_.fs; - opts_.fs_ = { - chmod: fs17.chmod ? (0, util_1.promisify)(fs17.chmod) : async () => { - }, - mkdir: (0, util_1.promisify)(fs17.mkdir), - readFile: (0, util_1.promisify)(fs17.readFile), - stat: (0, util_1.promisify)(fs17.stat), - unlink: (0, util_1.promisify)(fs17.unlink), - writeFile: (0, util_1.promisify)(fs17.writeFile) - }; - return opts_; - } - async function cmdShim2(src, to, opts) { - const opts_ = ingestOptions(opts); - await cmdShim_(src, to, opts_); - } - function cmdShimIfExists(src, to, opts) { - return cmdShim2(src, to, opts).catch(() => { - }); - } - function rm(path17, opts) { - return opts.fs_.unlink(path17).catch(() => { - }); - } - async function cmdShim_(src, to, opts) { - const srcRuntimeInfo = await searchScriptRuntime(src, opts); - await writeShimsPreCommon(to, opts); - return writeAllShims(src, to, srcRuntimeInfo, opts); - } - function writeShimsPreCommon(target, opts) { - return opts.fs_.mkdir(path16.dirname(target), { recursive: true }); - } - function writeAllShims(src, to, srcRuntimeInfo, opts) { - const opts_ = ingestOptions(opts); - const generatorAndExts = [{ generator: generateShShim, extension: "" }]; - if (opts_.createCmdFile) { - generatorAndExts.push({ generator: generateCmdShim, extension: CMD_EXTENSION }); + maxReadSize = 16 * 1024 * 1024; + PROCESS = Symbol("process"); + FILE2 = Symbol("file"); + DIRECTORY2 = Symbol("directory"); + SYMLINK2 = Symbol("symlink"); + HARDLINK2 = Symbol("hardlink"); + HEADER = Symbol("header"); + READ2 = Symbol("read"); + LSTAT = Symbol("lstat"); + ONLSTAT = Symbol("onlstat"); + ONREAD = Symbol("onread"); + ONREADLINK = Symbol("onreadlink"); + OPENFILE = Symbol("openfile"); + ONOPENFILE = Symbol("onopenfile"); + CLOSE = Symbol("close"); + MODE = Symbol("mode"); + AWAITDRAIN = Symbol("awaitDrain"); + ONDRAIN = Symbol("ondrain"); + PREFIX = Symbol("prefix"); + WriteEntry = class extends Minipass { + path; + portable; + myuid = process.getuid && process.getuid() || 0; + // until node has builtin pwnam functions, this'll have to do + myuser = process.env.USER || ""; + maxReadSize; + linkCache; + statCache; + preservePaths; + cwd; + strict; + mtime; + noPax; + noMtime; + prefix; + fd; + blockLen = 0; + blockRemain = 0; + buf; + pos = 0; + remain = 0; + length = 0; + offset = 0; + win32; + absolute; + header; + type; + linkpath; + stat; + onWriteEntry; + #hadError = false; + constructor(p, opt_ = {}) { + const opt = dealias(opt_); + super(); + this.path = normalizeWindowsPath(p); + this.portable = !!opt.portable; + this.maxReadSize = opt.maxReadSize || maxReadSize; + this.linkCache = opt.linkCache || /* @__PURE__ */ new Map(); + this.statCache = opt.statCache || /* @__PURE__ */ new Map(); + this.preservePaths = !!opt.preservePaths; + this.cwd = normalizeWindowsPath(opt.cwd || process.cwd()); + this.strict = !!opt.strict; + this.noPax = !!opt.noPax; + this.noMtime = !!opt.noMtime; + this.mtime = opt.mtime; + this.prefix = opt.prefix ? normalizeWindowsPath(opt.prefix) : void 0; + this.onWriteEntry = opt.onWriteEntry; + if (typeof opt.onwarn === "function") { + this.on("warn", opt.onwarn); + } + let pathWarn = false; + if (!this.preservePaths) { + const [root, stripped] = stripAbsolutePath(this.path); + if (root && typeof stripped === "string") { + this.path = stripped; + pathWarn = root; + } + } + this.win32 = !!opt.win32 || process.platform === "win32"; + if (this.win32) { + this.path = decode(this.path.replace(/\\/g, "/")); + p = p.replace(/\\/g, "/"); + } + this.absolute = normalizeWindowsPath(opt.absolute || import_path9.default.resolve(this.cwd, p)); + if (this.path === "") { + this.path = "./"; + } + if (pathWarn) { + this.warn("TAR_ENTRY_INFO", `stripping ${pathWarn} from absolute path`, { + entry: this, + path: pathWarn + this.path + }); + } + const cs = this.statCache.get(this.absolute); + if (cs) { + this[ONLSTAT](cs); + } else { + this[LSTAT](); + } } - if (opts_.createPwshFile) { - generatorAndExts.push({ generator: generatePwshShim, extension: ".ps1" }); + warn(code2, message, data = {}) { + return warnMethod(this, code2, message, data); } - return Promise.all(generatorAndExts.map((generatorAndExt) => writeShim(src, to + generatorAndExt.extension, srcRuntimeInfo, generatorAndExt.generator, opts_))); - } - function writeShimPre(target, opts) { - return rm(target, opts); - } - function writeShimPost(target, opts) { - return chmodShim(target, opts); - } - async function searchScriptRuntime(target, opts) { - try { - const data = await opts.fs_.readFile(target, "utf8"); - const firstLine = data.trim().split(/\r*\n/)[0]; - const shebang = firstLine.match(shebangExpr); - if (!shebang) { - const targetExtension = path16.extname(target).toLowerCase(); - return { - // undefined if extension is unknown but it's converted to null. - program: extensionToProgramMap.get(targetExtension) || null, - additionalArgs: "" - }; - } - return { - program: shebang[1], - additionalArgs: shebang[2] - }; - } catch (err) { - if (!isWindows4() || err.code !== "ENOENT") - throw err; - if (await opts.fs_.stat(`${target}${getExeExtension()}`)) { - return { - program: null, - additionalArgs: "" - }; + emit(ev, ...data) { + if (ev === "error") { + this.#hadError = true; } - throw err; + return super.emit(ev, ...data); } - } - function getExeExtension() { - let cmdExtension; - if (process.env.PATHEXT) { - cmdExtension = process.env.PATHEXT.split(path16.delimiter).find((ext) => ext.toLowerCase() === ".exe"); + [LSTAT]() { + import_fs11.default.lstat(this.absolute, (er, stat) => { + if (er) { + return this.emit("error", er); + } + this[ONLSTAT](stat); + }); } - return cmdExtension || ".exe"; - } - async function writeShim(src, to, srcRuntimeInfo, generateShimScript, opts) { - const defaultArgs = opts.preserveSymlinks ? "--preserve-symlinks" : ""; - const args = [srcRuntimeInfo.additionalArgs, defaultArgs].filter((arg) => arg).join(" "); - opts = Object.assign({}, opts, { - prog: srcRuntimeInfo.program, - args - }); - await writeShimPre(to, opts); - await opts.fs_.writeFile(to, generateShimScript(src, to, opts), "utf8"); - return writeShimPost(to, opts); - } - function generateCmdShim(src, to, opts) { - const shTarget = path16.relative(path16.dirname(to), src); - let target = shTarget.split("/").join("\\"); - const quotedPathToTarget = path16.isAbsolute(target) ? `"${target}"` : `"%~dp0\\${target}"`; - let longProg; - let prog = opts.prog; - let args = opts.args || ""; - const nodePath = normalizePathEnvVar(opts.nodePath).win32; - const prependToPath = normalizePathEnvVar(opts.prependToPath).win32; - if (!prog) { - prog = quotedPathToTarget; - args = ""; - target = ""; - } else if (prog === "node" && opts.nodeExecPath) { - prog = `"${opts.nodeExecPath}"`; - target = quotedPathToTarget; - } else { - longProg = `"%~dp0\\${prog}.exe"`; - target = quotedPathToTarget; + [ONLSTAT](stat) { + this.statCache.set(this.absolute, stat); + this.stat = stat; + if (!stat.isFile()) { + stat.size = 0; + } + this.type = getType(stat); + this.emit("stat", stat); + this[PROCESS](); } - let progArgs = opts.progArgs ? `${opts.progArgs.join(` `)} ` : ""; - let cmd = "@SETLOCAL\r\n"; - if (prependToPath) { - cmd += `@SET "PATH=${prependToPath}:%PATH%"\r -`; + [PROCESS]() { + switch (this.type) { + case "File": + return this[FILE2](); + case "Directory": + return this[DIRECTORY2](); + case "SymbolicLink": + return this[SYMLINK2](); + // unsupported types are ignored. + default: + return this.end(); + } } - if (nodePath) { - cmd += `@IF NOT DEFINED NODE_PATH (\r - @SET "NODE_PATH=${nodePath}"\r -) ELSE (\r - @SET "NODE_PATH=${nodePath};%NODE_PATH%"\r -)\r -`; + [MODE](mode) { + return modeFix(mode, this.type === "Directory", this.portable); } - if (longProg) { - cmd += `@IF EXIST ${longProg} (\r - ${longProg} ${args} ${target} ${progArgs}%*\r -) ELSE (\r - @SET PATHEXT=%PATHEXT:;.JS;=;%\r - ${prog} ${args} ${target} ${progArgs}%*\r -)\r -`; - } else { - cmd += `@${prog} ${args} ${target} ${progArgs}%*\r -`; + [PREFIX](path16) { + return prefixPath(path16, this.prefix); } - return cmd; - } - function generateShShim(src, to, opts) { - let shTarget = path16.relative(path16.dirname(to), src); - let shProg = opts.prog && opts.prog.split("\\").join("/"); - let shLongProg; - shTarget = shTarget.split("\\").join("/"); - const quotedPathToTarget = path16.isAbsolute(shTarget) ? `"${shTarget}"` : `"$basedir/${shTarget}"`; - let args = opts.args || ""; - const shNodePath = normalizePathEnvVar(opts.nodePath).posix; - if (!shProg) { - shProg = quotedPathToTarget; - args = ""; - shTarget = ""; - } else if (opts.prog === "node" && opts.nodeExecPath) { - shProg = `"${opts.nodeExecPath}"`; - shTarget = quotedPathToTarget; - } else { - shLongProg = `"$basedir/${opts.prog}"`; - shTarget = quotedPathToTarget; + [HEADER]() { + if (!this.stat) { + throw new Error("cannot write header before stat"); + } + if (this.type === "Directory" && this.portable) { + this.noMtime = true; + } + this.onWriteEntry?.(this); + this.header = new Header({ + path: this[PREFIX](this.path), + // only apply the prefix to hard links. + linkpath: this.type === "Link" && this.linkpath !== void 0 ? this[PREFIX](this.linkpath) : this.linkpath, + // only the permissions and setuid/setgid/sticky bitflags + // not the higher-order bits that specify file type + mode: this[MODE](this.stat.mode), + uid: this.portable ? void 0 : this.stat.uid, + gid: this.portable ? void 0 : this.stat.gid, + size: this.stat.size, + mtime: this.noMtime ? void 0 : this.mtime || this.stat.mtime, + /* c8 ignore next */ + type: this.type === "Unsupported" ? void 0 : this.type, + uname: this.portable ? void 0 : this.stat.uid === this.myuid ? this.myuser : "", + atime: this.portable ? void 0 : this.stat.atime, + ctime: this.portable ? void 0 : this.stat.ctime + }); + if (this.header.encode() && !this.noPax) { + super.write(new Pax({ + atime: this.portable ? void 0 : this.header.atime, + ctime: this.portable ? void 0 : this.header.ctime, + gid: this.portable ? void 0 : this.header.gid, + mtime: this.noMtime ? void 0 : this.mtime || this.header.mtime, + path: this[PREFIX](this.path), + linkpath: this.type === "Link" && this.linkpath !== void 0 ? this[PREFIX](this.linkpath) : this.linkpath, + size: this.header.size, + uid: this.portable ? void 0 : this.header.uid, + uname: this.portable ? void 0 : this.header.uname, + dev: this.portable ? void 0 : this.stat.dev, + ino: this.portable ? void 0 : this.stat.ino, + nlink: this.portable ? void 0 : this.stat.nlink + }).encode()); + } + const block = this.header?.block; + if (!block) { + throw new Error("failed to encode header"); + } + super.write(block); } - let progArgs = opts.progArgs ? `${opts.progArgs.join(` `)} ` : ""; - let sh = `#!/bin/sh -basedir=$(dirname "$(echo "$0" | sed -e 's,\\\\,/,g')") - -case \`uname\` in - *CYGWIN*) basedir=\`cygpath -w "$basedir"\`;; -esac - -`; - if (opts.prependToPath) { - sh += `export PATH="${opts.prependToPath}:$PATH" -`; + [DIRECTORY2]() { + if (!this.stat) { + throw new Error("cannot create directory entry without stat"); + } + if (this.path.slice(-1) !== "/") { + this.path += "/"; + } + this.stat.size = 0; + this[HEADER](); + this.end(); } - if (shNodePath) { - sh += `if [ -z "$NODE_PATH" ]; then - export NODE_PATH="${shNodePath}" -else - export NODE_PATH="${shNodePath}:$NODE_PATH" -fi -`; + [SYMLINK2]() { + import_fs11.default.readlink(this.absolute, (er, linkpath) => { + if (er) { + return this.emit("error", er); + } + this[ONREADLINK](linkpath); + }); } - if (shLongProg) { - sh += `if [ -x ${shLongProg} ]; then - exec ${shLongProg} ${args} ${shTarget} ${progArgs}"$@" -else - exec ${shProg} ${args} ${shTarget} ${progArgs}"$@" -fi -`; - } else { - sh += `${shProg} ${args} ${shTarget} ${progArgs}"$@" -exit $? -`; + [ONREADLINK](linkpath) { + this.linkpath = normalizeWindowsPath(linkpath); + this[HEADER](); + this.end(); } - return sh; - } - function generatePwshShim(src, to, opts) { - let shTarget = path16.relative(path16.dirname(to), src); - const shProg = opts.prog && opts.prog.split("\\").join("/"); - let pwshProg = shProg && `"${shProg}$exe"`; - let pwshLongProg; - shTarget = shTarget.split("\\").join("/"); - const quotedPathToTarget = path16.isAbsolute(shTarget) ? `"${shTarget}"` : `"$basedir/${shTarget}"`; - let args = opts.args || ""; - let normalizedNodePathEnvVar = normalizePathEnvVar(opts.nodePath); - const nodePath = normalizedNodePathEnvVar.win32; - const shNodePath = normalizedNodePathEnvVar.posix; - let normalizedPrependPathEnvVar = normalizePathEnvVar(opts.prependToPath); - const prependPath = normalizedPrependPathEnvVar.win32; - const shPrependPath = normalizedPrependPathEnvVar.posix; - if (!pwshProg) { - pwshProg = quotedPathToTarget; - args = ""; - shTarget = ""; - } else if (opts.prog === "node" && opts.nodeExecPath) { - pwshProg = `"${opts.nodeExecPath}"`; - shTarget = quotedPathToTarget; - } else { - pwshLongProg = `"$basedir/${opts.prog}$exe"`; - shTarget = quotedPathToTarget; + [HARDLINK2](linkpath) { + if (!this.stat) { + throw new Error("cannot create link entry without stat"); + } + this.type = "Link"; + this.linkpath = normalizeWindowsPath(import_path9.default.relative(this.cwd, linkpath)); + this.stat.size = 0; + this[HEADER](); + this.end(); } - let progArgs = opts.progArgs ? `${opts.progArgs.join(` `)} ` : ""; - let pwsh = `#!/usr/bin/env pwsh -$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent - -$exe="" -${nodePath || prependPath ? '$pathsep=":"\n' : ""}${nodePath ? `$env_node_path=$env:NODE_PATH -$new_node_path="${nodePath}" -` : ""}${prependPath ? `$env_path=$env:PATH -$prepend_path="${prependPath}" -` : ""}if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) { - # Fix case when both the Windows and Linux builds of Node - # are installed in the same directory - $exe=".exe" -${nodePath || prependPath ? ' $pathsep=";"\n' : ""}}`; - if (shNodePath || shPrependPath) { - pwsh += ` else { -${shNodePath ? ` $new_node_path="${shNodePath}" -` : ""}${shPrependPath ? ` $prepend_path="${shPrependPath}" -` : ""}} -`; + [FILE2]() { + if (!this.stat) { + throw new Error("cannot create file entry without stat"); + } + if (this.stat.nlink > 1) { + const linkKey = `${this.stat.dev}:${this.stat.ino}`; + const linkpath = this.linkCache.get(linkKey); + if (linkpath?.indexOf(this.cwd) === 0) { + return this[HARDLINK2](linkpath); + } + this.linkCache.set(linkKey, this.absolute); + } + this[HEADER](); + if (this.stat.size === 0) { + return this.end(); + } + this[OPENFILE](); } - if (shNodePath) { - pwsh += `if ([string]::IsNullOrEmpty($env_node_path)) { - $env:NODE_PATH=$new_node_path -} else { - $env:NODE_PATH="$new_node_path$pathsep$env_node_path" -} -`; + [OPENFILE]() { + import_fs11.default.open(this.absolute, "r", (er, fd) => { + if (er) { + return this.emit("error", er); + } + this[ONOPENFILE](fd); + }); } - if (opts.prependToPath) { - pwsh += ` -$env:PATH="$prepend_path$pathsep$env:PATH" -`; + [ONOPENFILE](fd) { + this.fd = fd; + if (this.#hadError) { + return this[CLOSE](); + } + if (!this.stat) { + throw new Error("should stat before calling onopenfile"); + } + this.blockLen = 512 * Math.ceil(this.stat.size / 512); + this.blockRemain = this.blockLen; + const bufLen = Math.min(this.blockLen, this.maxReadSize); + this.buf = Buffer.allocUnsafe(bufLen); + this.offset = 0; + this.pos = 0; + this.remain = this.stat.size; + this.length = this.buf.length; + this[READ2](); } - if (pwshLongProg) { - pwsh += ` -$ret=0 -if (Test-Path ${pwshLongProg}) { - # Support pipeline input - if ($MyInvocation.ExpectingInput) { - $input | & ${pwshLongProg} ${args} ${shTarget} ${progArgs}$args - } else { - & ${pwshLongProg} ${args} ${shTarget} ${progArgs}$args - } - $ret=$LASTEXITCODE -} else { - # Support pipeline input - if ($MyInvocation.ExpectingInput) { - $input | & ${pwshProg} ${args} ${shTarget} ${progArgs}$args - } else { - & ${pwshProg} ${args} ${shTarget} ${progArgs}$args - } - $ret=$LASTEXITCODE -} -${nodePath ? "$env:NODE_PATH=$env_node_path\n" : ""}${prependPath ? "$env:PATH=$env_path\n" : ""}exit $ret -`; - } else { - pwsh += ` -# Support pipeline input -if ($MyInvocation.ExpectingInput) { - $input | & ${pwshProg} ${args} ${shTarget} ${progArgs}$args -} else { - & ${pwshProg} ${args} ${shTarget} ${progArgs}$args -} -${nodePath ? "$env:NODE_PATH=$env_node_path\n" : ""}${prependPath ? "$env:PATH=$env_path\n" : ""}exit $LASTEXITCODE -`; + [READ2]() { + const { fd, buf, offset, length, pos: pos2 } = this; + if (fd === void 0 || buf === void 0) { + throw new Error("cannot read file without first opening"); + } + import_fs11.default.read(fd, buf, offset, length, pos2, (er, bytesRead) => { + if (er) { + return this[CLOSE](() => this.emit("error", er)); + } + this[ONREAD](bytesRead); + }); } - return pwsh; - } - function chmodShim(to, opts) { - return opts.fs_.chmod(to, 493); - } - function normalizePathEnvVar(nodePath) { - if (!nodePath || !nodePath.length) { - return { - win32: "", - posix: "" - }; + /* c8 ignore start */ + [CLOSE](cb = () => { + }) { + if (this.fd !== void 0) + import_fs11.default.close(this.fd, cb); } - let split = typeof nodePath === "string" ? nodePath.split(path16.delimiter) : Array.from(nodePath); - let result = {}; - for (let i = 0; i < split.length; i++) { - const win322 = split[i].split("/").join("\\"); - const posix = isWindows4() ? split[i].split("\\").join("/").replace(/^([^:\\/]*):/, (_, $1) => `/mnt/${$1.toLowerCase()}`) : split[i]; - result.win32 = result.win32 ? `${result.win32};${win322}` : win322; - result.posix = result.posix ? `${result.posix}:${posix}` : posix; - result[i] = { win32: win322, posix }; + [ONREAD](bytesRead) { + if (bytesRead <= 0 && this.remain > 0) { + const er = Object.assign(new Error("encountered unexpected EOF"), { + path: this.absolute, + syscall: "read", + code: "EOF" + }); + return this[CLOSE](() => this.emit("error", er)); + } + if (bytesRead > this.remain) { + const er = Object.assign(new Error("did not encounter expected EOF"), { + path: this.absolute, + syscall: "read", + code: "EOF" + }); + return this[CLOSE](() => this.emit("error", er)); + } + if (!this.buf) { + throw new Error("should have created buffer prior to reading"); + } + if (bytesRead === this.remain) { + for (let i = bytesRead; i < this.length && bytesRead < this.blockRemain; i++) { + this.buf[i + this.offset] = 0; + bytesRead++; + this.remain++; + } + } + const chunk = this.offset === 0 && bytesRead === this.buf.length ? this.buf : this.buf.subarray(this.offset, this.offset + bytesRead); + const flushed = this.write(chunk); + if (!flushed) { + this[AWAITDRAIN](() => this[ONDRAIN]()); + } else { + this[ONDRAIN](); + } } - return result; - } - module2.exports = cmdShim2; - } -}); - -// .yarn/cache/tar-npm-7.4.3-1dbbd1ffc3-d4679609bb.zip/node_modules/tar/dist/esm/mode-fix.js -var modeFix; -var init_mode_fix = __esm({ - ".yarn/cache/tar-npm-7.4.3-1dbbd1ffc3-d4679609bb.zip/node_modules/tar/dist/esm/mode-fix.js"() { - modeFix = (mode, isDir, portable) => { - mode &= 4095; - if (portable) { - mode = (mode | 384) & ~18; + [AWAITDRAIN](cb) { + this.once("drain", cb); + } + write(chunk, encoding, cb) { + if (typeof encoding === "function") { + cb = encoding; + encoding = void 0; + } + if (typeof chunk === "string") { + chunk = Buffer.from(chunk, typeof encoding === "string" ? encoding : "utf8"); + } + if (this.blockRemain < chunk.length) { + const er = Object.assign(new Error("writing more data than expected"), { + path: this.absolute + }); + return this.emit("error", er); + } + this.remain -= chunk.length; + this.blockRemain -= chunk.length; + this.pos += chunk.length; + this.offset += chunk.length; + return super.write(chunk, null, cb); } - if (isDir) { - if (mode & 256) { - mode |= 64; + [ONDRAIN]() { + if (!this.remain) { + if (this.blockRemain) { + super.write(Buffer.alloc(this.blockRemain)); + } + return this[CLOSE]((er) => er ? this.emit("error", er) : this.end()); } - if (mode & 32) { - mode |= 8; + if (!this.buf) { + throw new Error("buffer lost somehow in ONDRAIN"); } - if (mode & 4) { - mode |= 1; + if (this.offset >= this.length) { + this.buf = Buffer.allocUnsafe(Math.min(this.blockRemain, this.buf.length)); + this.offset = 0; } + this.length = this.buf.length - this.offset; + this[READ2](); } - return mode; }; - } -}); - -// .yarn/cache/tar-npm-7.4.3-1dbbd1ffc3-d4679609bb.zip/node_modules/tar/dist/esm/write-entry.js -var import_fs14, import_path13, prefixPath, maxReadSize, PROCESS, FILE2, DIRECTORY2, SYMLINK2, HARDLINK2, HEADER, READ2, LSTAT, ONLSTAT, ONREAD, ONREADLINK, OPENFILE, ONOPENFILE, CLOSE, MODE, AWAITDRAIN, ONDRAIN, PREFIX, WriteEntry, WriteEntrySync, WriteEntryTar, getType; -var init_write_entry = __esm({ - ".yarn/cache/tar-npm-7.4.3-1dbbd1ffc3-d4679609bb.zip/node_modules/tar/dist/esm/write-entry.js"() { - import_fs14 = __toESM(require("fs"), 1); - init_esm(); - import_path13 = __toESM(require("path"), 1); - init_header(); - init_mode_fix(); - init_normalize_windows_path(); - init_options(); - init_pax(); - init_strip_absolute_path(); - init_strip_trailing_slashes(); - init_warn_method(); - init_winchars(); - prefixPath = (path16, prefix) => { - if (!prefix) { - return normalizeWindowsPath(path16); + WriteEntrySync = class extends WriteEntry { + sync = true; + [LSTAT]() { + this[ONLSTAT](import_fs11.default.lstatSync(this.absolute)); + } + [SYMLINK2]() { + this[ONREADLINK](import_fs11.default.readlinkSync(this.absolute)); + } + [OPENFILE]() { + this[ONOPENFILE](import_fs11.default.openSync(this.absolute, "r")); + } + [READ2]() { + let threw = true; + try { + const { fd, buf, offset, length, pos: pos2 } = this; + if (fd === void 0 || buf === void 0) { + throw new Error("fd and buf must be set in READ method"); + } + const bytesRead = import_fs11.default.readSync(fd, buf, offset, length, pos2); + this[ONREAD](bytesRead); + threw = false; + } finally { + if (threw) { + try { + this[CLOSE](() => { + }); + } catch (er) { + } + } + } + } + [AWAITDRAIN](cb) { + cb(); + } + /* c8 ignore start */ + [CLOSE](cb = () => { + }) { + if (this.fd !== void 0) + import_fs11.default.closeSync(this.fd); + cb(); } - path16 = normalizeWindowsPath(path16).replace(/^\.(\/|$)/, ""); - return stripTrailingSlashes(prefix) + "/" + path16; }; - maxReadSize = 16 * 1024 * 1024; - PROCESS = Symbol("process"); - FILE2 = Symbol("file"); - DIRECTORY2 = Symbol("directory"); - SYMLINK2 = Symbol("symlink"); - HARDLINK2 = Symbol("hardlink"); - HEADER = Symbol("header"); - READ2 = Symbol("read"); - LSTAT = Symbol("lstat"); - ONLSTAT = Symbol("onlstat"); - ONREAD = Symbol("onread"); - ONREADLINK = Symbol("onreadlink"); - OPENFILE = Symbol("openfile"); - ONOPENFILE = Symbol("onopenfile"); - CLOSE = Symbol("close"); - MODE = Symbol("mode"); - AWAITDRAIN = Symbol("awaitDrain"); - ONDRAIN = Symbol("ondrain"); - PREFIX = Symbol("prefix"); - WriteEntry = class extends Minipass { - path; - portable; - myuid = process.getuid && process.getuid() || 0; - // until node has builtin pwnam functions, this'll have to do - myuser = process.env.USER || ""; - maxReadSize; - linkCache; - statCache; - preservePaths; - cwd; - strict; - mtime; - noPax; - noMtime; - prefix; - fd; + WriteEntryTar = class extends Minipass { blockLen = 0; blockRemain = 0; - buf; + buf = 0; pos = 0; remain = 0; length = 0; - offset = 0; - win32; - absolute; - header; + preservePaths; + portable; + strict; + noPax; + noMtime; + readEntry; type; + prefix; + path; + mode; + uid; + gid; + uname; + gname; + header; + mtime; + atime; + ctime; linkpath; - stat; + size; onWriteEntry; - #hadError = false; - constructor(p, opt_ = {}) { + warn(code2, message, data = {}) { + return warnMethod(this, code2, message, data); + } + constructor(readEntry, opt_ = {}) { const opt = dealias(opt_); super(); - this.path = normalizeWindowsPath(p); - this.portable = !!opt.portable; - this.maxReadSize = opt.maxReadSize || maxReadSize; - this.linkCache = opt.linkCache || /* @__PURE__ */ new Map(); - this.statCache = opt.statCache || /* @__PURE__ */ new Map(); this.preservePaths = !!opt.preservePaths; - this.cwd = normalizeWindowsPath(opt.cwd || process.cwd()); + this.portable = !!opt.portable; this.strict = !!opt.strict; this.noPax = !!opt.noPax; this.noMtime = !!opt.noMtime; - this.mtime = opt.mtime; - this.prefix = opt.prefix ? normalizeWindowsPath(opt.prefix) : void 0; this.onWriteEntry = opt.onWriteEntry; - if (typeof opt.onwarn === "function") { - this.on("warn", opt.onwarn); - } - let pathWarn = false; - if (!this.preservePaths) { - const [root, stripped] = stripAbsolutePath(this.path); - if (root && typeof stripped === "string") { - this.path = stripped; - pathWarn = root; - } - } - this.win32 = !!opt.win32 || process.platform === "win32"; - if (this.win32) { - this.path = decode(this.path.replace(/\\/g, "/")); - p = p.replace(/\\/g, "/"); - } - this.absolute = normalizeWindowsPath(opt.absolute || import_path13.default.resolve(this.cwd, p)); - if (this.path === "") { - this.path = "./"; - } - if (pathWarn) { - this.warn("TAR_ENTRY_INFO", `stripping ${pathWarn} from absolute path`, { - entry: this, - path: pathWarn + this.path - }); - } - const cs = this.statCache.get(this.absolute); - if (cs) { - this[ONLSTAT](cs); - } else { - this[LSTAT](); - } - } - warn(code2, message, data = {}) { - return warnMethod(this, code2, message, data); - } - emit(ev, ...data) { - if (ev === "error") { - this.#hadError = true; - } - return super.emit(ev, ...data); - } - [LSTAT]() { - import_fs14.default.lstat(this.absolute, (er, stat2) => { - if (er) { - return this.emit("error", er); - } - this[ONLSTAT](stat2); - }); - } - [ONLSTAT](stat2) { - this.statCache.set(this.absolute, stat2); - this.stat = stat2; - if (!stat2.isFile()) { - stat2.size = 0; + this.readEntry = readEntry; + const { type } = readEntry; + if (type === "Unsupported") { + throw new Error("writing entry that should be ignored"); } - this.type = getType(stat2); - this.emit("stat", stat2); - this[PROCESS](); - } - [PROCESS]() { - switch (this.type) { - case "File": - return this[FILE2](); - case "Directory": - return this[DIRECTORY2](); - case "SymbolicLink": - return this[SYMLINK2](); - // unsupported types are ignored. - default: - return this.end(); + this.type = type; + if (this.type === "Directory" && this.portable) { + this.noMtime = true; } - } - [MODE](mode) { - return modeFix(mode, this.type === "Directory", this.portable); - } - [PREFIX](path16) { - return prefixPath(path16, this.prefix); - } - [HEADER]() { - if (!this.stat) { - throw new Error("cannot write header before stat"); + this.prefix = opt.prefix; + this.path = normalizeWindowsPath(readEntry.path); + this.mode = readEntry.mode !== void 0 ? this[MODE](readEntry.mode) : void 0; + this.uid = this.portable ? void 0 : readEntry.uid; + this.gid = this.portable ? void 0 : readEntry.gid; + this.uname = this.portable ? void 0 : readEntry.uname; + this.gname = this.portable ? void 0 : readEntry.gname; + this.size = readEntry.size; + this.mtime = this.noMtime ? void 0 : opt.mtime || readEntry.mtime; + this.atime = this.portable ? void 0 : readEntry.atime; + this.ctime = this.portable ? void 0 : readEntry.ctime; + this.linkpath = readEntry.linkpath !== void 0 ? normalizeWindowsPath(readEntry.linkpath) : void 0; + if (typeof opt.onwarn === "function") { + this.on("warn", opt.onwarn); } - if (this.type === "Directory" && this.portable) { - this.noMtime = true; + let pathWarn = false; + if (!this.preservePaths) { + const [root, stripped] = stripAbsolutePath(this.path); + if (root && typeof stripped === "string") { + this.path = stripped; + pathWarn = root; + } } + this.remain = readEntry.size; + this.blockRemain = readEntry.startBlockSize; this.onWriteEntry?.(this); this.header = new Header({ path: this[PREFIX](this.path), - // only apply the prefix to hard links. linkpath: this.type === "Link" && this.linkpath !== void 0 ? this[PREFIX](this.linkpath) : this.linkpath, // only the permissions and setuid/setgid/sticky bitflags // not the higher-order bits that specify file type - mode: this[MODE](this.stat.mode), - uid: this.portable ? void 0 : this.stat.uid, - gid: this.portable ? void 0 : this.stat.gid, - size: this.stat.size, - mtime: this.noMtime ? void 0 : this.mtime || this.stat.mtime, - /* c8 ignore next */ - type: this.type === "Unsupported" ? void 0 : this.type, - uname: this.portable ? void 0 : this.stat.uid === this.myuid ? this.myuser : "", - atime: this.portable ? void 0 : this.stat.atime, - ctime: this.portable ? void 0 : this.stat.ctime + mode: this.mode, + uid: this.portable ? void 0 : this.uid, + gid: this.portable ? void 0 : this.gid, + size: this.size, + mtime: this.noMtime ? void 0 : this.mtime, + type: this.type, + uname: this.portable ? void 0 : this.uname, + atime: this.portable ? void 0 : this.atime, + ctime: this.portable ? void 0 : this.ctime }); + if (pathWarn) { + this.warn("TAR_ENTRY_INFO", `stripping ${pathWarn} from absolute path`, { + entry: this, + path: pathWarn + this.path + }); + } if (this.header.encode() && !this.noPax) { super.write(new Pax({ - atime: this.portable ? void 0 : this.header.atime, - ctime: this.portable ? void 0 : this.header.ctime, - gid: this.portable ? void 0 : this.header.gid, - mtime: this.noMtime ? void 0 : this.mtime || this.header.mtime, + atime: this.portable ? void 0 : this.atime, + ctime: this.portable ? void 0 : this.ctime, + gid: this.portable ? void 0 : this.gid, + mtime: this.noMtime ? void 0 : this.mtime, path: this[PREFIX](this.path), linkpath: this.type === "Link" && this.linkpath !== void 0 ? this[PREFIX](this.linkpath) : this.linkpath, - size: this.header.size, - uid: this.portable ? void 0 : this.header.uid, - uname: this.portable ? void 0 : this.header.uname, - dev: this.portable ? void 0 : this.stat.dev, - ino: this.portable ? void 0 : this.stat.ino, - nlink: this.portable ? void 0 : this.stat.nlink + size: this.size, + uid: this.portable ? void 0 : this.uid, + uname: this.portable ? void 0 : this.uname, + dev: this.portable ? void 0 : this.readEntry.dev, + ino: this.portable ? void 0 : this.readEntry.ino, + nlink: this.portable ? void 0 : this.readEntry.nlink }).encode()); } - const block = this.header?.block; - if (!block) { + const b = this.header?.block; + if (!b) throw new Error("failed to encode header"); + super.write(b); + readEntry.pipe(this); + } + [PREFIX](path16) { + return prefixPath(path16, this.prefix); + } + [MODE](mode) { + return modeFix(mode, this.type === "Directory", this.portable); + } + write(chunk, encoding, cb) { + if (typeof encoding === "function") { + cb = encoding; + encoding = void 0; } - super.write(block); + if (typeof chunk === "string") { + chunk = Buffer.from(chunk, typeof encoding === "string" ? encoding : "utf8"); + } + const writeLen = chunk.length; + if (writeLen > this.blockRemain) { + throw new Error("writing more to entry than is appropriate"); + } + this.blockRemain -= writeLen; + return super.write(chunk, cb); } - [DIRECTORY2]() { - if (!this.stat) { - throw new Error("cannot create directory entry without stat"); + end(chunk, encoding, cb) { + if (this.blockRemain) { + super.write(Buffer.alloc(this.blockRemain)); } - if (this.path.slice(-1) !== "/") { - this.path += "/"; + if (typeof chunk === "function") { + cb = chunk; + encoding = void 0; + chunk = void 0; } - this.stat.size = 0; - this[HEADER](); - this.end(); + if (typeof encoding === "function") { + cb = encoding; + encoding = void 0; + } + if (typeof chunk === "string") { + chunk = Buffer.from(chunk, encoding ?? "utf8"); + } + if (cb) + this.once("finish", cb); + chunk ? super.end(chunk, cb) : super.end(cb); + return this; } - [SYMLINK2]() { - import_fs14.default.readlink(this.absolute, (er, linkpath) => { - if (er) { - return this.emit("error", er); - } - this[ONREADLINK](linkpath); - }); + }; + getType = (stat) => stat.isFile() ? "File" : stat.isDirectory() ? "Directory" : stat.isSymbolicLink() ? "SymbolicLink" : "Unsupported"; + } +}); + +// .yarn/cache/yallist-npm-5.0.0-8732dd9f1c-a499c81ce6.zip/node_modules/yallist/dist/esm/index.js +function insertAfter(self2, node, value) { + const prev = node; + const next = node ? node.next : self2.head; + const inserted = new Node(value, prev, next, self2); + if (inserted.next === void 0) { + self2.tail = inserted; + } + if (inserted.prev === void 0) { + self2.head = inserted; + } + self2.length++; + return inserted; +} +function push(self2, item) { + self2.tail = new Node(item, self2.tail, void 0, self2); + if (!self2.head) { + self2.head = self2.tail; + } + self2.length++; +} +function unshift(self2, item) { + self2.head = new Node(item, void 0, self2.head, self2); + if (!self2.tail) { + self2.tail = self2.head; + } + self2.length++; +} +var Yallist, Node; +var init_esm5 = __esm({ + ".yarn/cache/yallist-npm-5.0.0-8732dd9f1c-a499c81ce6.zip/node_modules/yallist/dist/esm/index.js"() { + Yallist = class _Yallist { + tail; + head; + length = 0; + static create(list2 = []) { + return new _Yallist(list2); } - [ONREADLINK](linkpath) { - this.linkpath = normalizeWindowsPath(linkpath); - this[HEADER](); - this.end(); + constructor(list2 = []) { + for (const item of list2) { + this.push(item); + } } - [HARDLINK2](linkpath) { - if (!this.stat) { - throw new Error("cannot create link entry without stat"); + *[Symbol.iterator]() { + for (let walker = this.head; walker; walker = walker.next) { + yield walker.value; } - this.type = "Link"; - this.linkpath = normalizeWindowsPath(import_path13.default.relative(this.cwd, linkpath)); - this.stat.size = 0; - this[HEADER](); - this.end(); } - [FILE2]() { - if (!this.stat) { - throw new Error("cannot create file entry without stat"); + removeNode(node) { + if (node.list !== this) { + throw new Error("removing node which does not belong to this list"); } - if (this.stat.nlink > 1) { - const linkKey = `${this.stat.dev}:${this.stat.ino}`; - const linkpath = this.linkCache.get(linkKey); - if (linkpath?.indexOf(this.cwd) === 0) { - return this[HARDLINK2](linkpath); - } - this.linkCache.set(linkKey, this.absolute); + const next = node.next; + const prev = node.prev; + if (next) { + next.prev = prev; } - this[HEADER](); - if (this.stat.size === 0) { - return this.end(); + if (prev) { + prev.next = next; } - this[OPENFILE](); - } - [OPENFILE]() { - import_fs14.default.open(this.absolute, "r", (er, fd) => { - if (er) { - return this.emit("error", er); - } - this[ONOPENFILE](fd); - }); - } - [ONOPENFILE](fd) { - this.fd = fd; - if (this.#hadError) { - return this[CLOSE](); + if (node === this.head) { + this.head = next; } - if (!this.stat) { - throw new Error("should stat before calling onopenfile"); + if (node === this.tail) { + this.tail = prev; } - this.blockLen = 512 * Math.ceil(this.stat.size / 512); - this.blockRemain = this.blockLen; - const bufLen = Math.min(this.blockLen, this.maxReadSize); - this.buf = Buffer.allocUnsafe(bufLen); - this.offset = 0; - this.pos = 0; - this.remain = this.stat.size; - this.length = this.buf.length; - this[READ2](); + this.length--; + node.next = void 0; + node.prev = void 0; + node.list = void 0; + return next; } - [READ2]() { - const { fd, buf, offset, length, pos: pos2 } = this; - if (fd === void 0 || buf === void 0) { - throw new Error("cannot read file without first opening"); + unshiftNode(node) { + if (node === this.head) { + return; + } + if (node.list) { + node.list.removeNode(node); + } + const head = this.head; + node.list = this; + node.next = head; + if (head) { + head.prev = node; + } + this.head = node; + if (!this.tail) { + this.tail = node; } - import_fs14.default.read(fd, buf, offset, length, pos2, (er, bytesRead) => { - if (er) { - return this[CLOSE](() => this.emit("error", er)); - } - this[ONREAD](bytesRead); - }); - } - /* c8 ignore start */ - [CLOSE](cb = () => { - }) { - if (this.fd !== void 0) - import_fs14.default.close(this.fd, cb); + this.length++; } - [ONREAD](bytesRead) { - if (bytesRead <= 0 && this.remain > 0) { - const er = Object.assign(new Error("encountered unexpected EOF"), { - path: this.absolute, - syscall: "read", - code: "EOF" - }); - return this[CLOSE](() => this.emit("error", er)); - } - if (bytesRead > this.remain) { - const er = Object.assign(new Error("did not encounter expected EOF"), { - path: this.absolute, - syscall: "read", - code: "EOF" - }); - return this[CLOSE](() => this.emit("error", er)); + pushNode(node) { + if (node === this.tail) { + return; } - if (!this.buf) { - throw new Error("should have created buffer prior to reading"); + if (node.list) { + node.list.removeNode(node); } - if (bytesRead === this.remain) { - for (let i = bytesRead; i < this.length && bytesRead < this.blockRemain; i++) { - this.buf[i + this.offset] = 0; - bytesRead++; - this.remain++; - } + const tail = this.tail; + node.list = this; + node.prev = tail; + if (tail) { + tail.next = node; } - const chunk = this.offset === 0 && bytesRead === this.buf.length ? this.buf : this.buf.subarray(this.offset, this.offset + bytesRead); - const flushed = this.write(chunk); - if (!flushed) { - this[AWAITDRAIN](() => this[ONDRAIN]()); - } else { - this[ONDRAIN](); + this.tail = node; + if (!this.head) { + this.head = node; } + this.length++; } - [AWAITDRAIN](cb) { - this.once("drain", cb); + push(...args) { + for (let i = 0, l = args.length; i < l; i++) { + push(this, args[i]); + } + return this.length; } - write(chunk, encoding, cb) { - if (typeof encoding === "function") { - cb = encoding; - encoding = void 0; + unshift(...args) { + for (var i = 0, l = args.length; i < l; i++) { + unshift(this, args[i]); } - if (typeof chunk === "string") { - chunk = Buffer.from(chunk, typeof encoding === "string" ? encoding : "utf8"); + return this.length; + } + pop() { + if (!this.tail) { + return void 0; } - if (this.blockRemain < chunk.length) { - const er = Object.assign(new Error("writing more data than expected"), { - path: this.absolute - }); - return this.emit("error", er); + const res = this.tail.value; + const t = this.tail; + this.tail = this.tail.prev; + if (this.tail) { + this.tail.next = void 0; + } else { + this.head = void 0; } - this.remain -= chunk.length; - this.blockRemain -= chunk.length; - this.pos += chunk.length; - this.offset += chunk.length; - return super.write(chunk, null, cb); + t.list = void 0; + this.length--; + return res; } - [ONDRAIN]() { - if (!this.remain) { - if (this.blockRemain) { - super.write(Buffer.alloc(this.blockRemain)); - } - return this[CLOSE]((er) => er ? this.emit("error", er) : this.end()); - } - if (!this.buf) { - throw new Error("buffer lost somehow in ONDRAIN"); + shift() { + if (!this.head) { + return void 0; } - if (this.offset >= this.length) { - this.buf = Buffer.allocUnsafe(Math.min(this.blockRemain, this.buf.length)); - this.offset = 0; + const res = this.head.value; + const h = this.head; + this.head = this.head.next; + if (this.head) { + this.head.prev = void 0; + } else { + this.tail = void 0; } - this.length = this.buf.length - this.offset; - this[READ2](); - } - }; - WriteEntrySync = class extends WriteEntry { - sync = true; - [LSTAT]() { - this[ONLSTAT](import_fs14.default.lstatSync(this.absolute)); - } - [SYMLINK2]() { - this[ONREADLINK](import_fs14.default.readlinkSync(this.absolute)); + h.list = void 0; + this.length--; + return res; } - [OPENFILE]() { - this[ONOPENFILE](import_fs14.default.openSync(this.absolute, "r")); + forEach(fn2, thisp) { + thisp = thisp || this; + for (let walker = this.head, i = 0; !!walker; i++) { + fn2.call(thisp, walker.value, i, this); + walker = walker.next; + } } - [READ2]() { - let threw = true; - try { - const { fd, buf, offset, length, pos: pos2 } = this; - if (fd === void 0 || buf === void 0) { - throw new Error("fd and buf must be set in READ method"); - } - const bytesRead = import_fs14.default.readSync(fd, buf, offset, length, pos2); - this[ONREAD](bytesRead); - threw = false; - } finally { - if (threw) { - try { - this[CLOSE](() => { - }); - } catch (er) { - } - } + forEachReverse(fn2, thisp) { + thisp = thisp || this; + for (let walker = this.tail, i = this.length - 1; !!walker; i--) { + fn2.call(thisp, walker.value, i, this); + walker = walker.prev; } } - [AWAITDRAIN](cb) { - cb(); + get(n) { + let i = 0; + let walker = this.head; + for (; !!walker && i < n; i++) { + walker = walker.next; + } + if (i === n && !!walker) { + return walker.value; + } } - /* c8 ignore start */ - [CLOSE](cb = () => { - }) { - if (this.fd !== void 0) - import_fs14.default.closeSync(this.fd); - cb(); + getReverse(n) { + let i = 0; + let walker = this.tail; + for (; !!walker && i < n; i++) { + walker = walker.prev; + } + if (i === n && !!walker) { + return walker.value; + } } - }; - WriteEntryTar = class extends Minipass { - blockLen = 0; - blockRemain = 0; - buf = 0; - pos = 0; - remain = 0; - length = 0; - preservePaths; - portable; - strict; - noPax; - noMtime; - readEntry; - type; - prefix; - path; - mode; - uid; - gid; - uname; - gname; - header; - mtime; - atime; - ctime; - linkpath; - size; - onWriteEntry; - warn(code2, message, data = {}) { - return warnMethod(this, code2, message, data); + map(fn2, thisp) { + thisp = thisp || this; + const res = new _Yallist(); + for (let walker = this.head; !!walker; ) { + res.push(fn2.call(thisp, walker.value, this)); + walker = walker.next; + } + return res; } - constructor(readEntry, opt_ = {}) { - const opt = dealias(opt_); - super(); - this.preservePaths = !!opt.preservePaths; - this.portable = !!opt.portable; - this.strict = !!opt.strict; - this.noPax = !!opt.noPax; - this.noMtime = !!opt.noMtime; - this.onWriteEntry = opt.onWriteEntry; - this.readEntry = readEntry; - const { type } = readEntry; - if (type === "Unsupported") { - throw new Error("writing entry that should be ignored"); + mapReverse(fn2, thisp) { + thisp = thisp || this; + var res = new _Yallist(); + for (let walker = this.tail; !!walker; ) { + res.push(fn2.call(thisp, walker.value, this)); + walker = walker.prev; } - this.type = type; - if (this.type === "Directory" && this.portable) { - this.noMtime = true; + return res; + } + reduce(fn2, initial) { + let acc; + let walker = this.head; + if (arguments.length > 1) { + acc = initial; + } else if (this.head) { + walker = this.head.next; + acc = this.head.value; + } else { + throw new TypeError("Reduce of empty list with no initial value"); } - this.prefix = opt.prefix; - this.path = normalizeWindowsPath(readEntry.path); - this.mode = readEntry.mode !== void 0 ? this[MODE](readEntry.mode) : void 0; - this.uid = this.portable ? void 0 : readEntry.uid; - this.gid = this.portable ? void 0 : readEntry.gid; - this.uname = this.portable ? void 0 : readEntry.uname; - this.gname = this.portable ? void 0 : readEntry.gname; - this.size = readEntry.size; - this.mtime = this.noMtime ? void 0 : opt.mtime || readEntry.mtime; - this.atime = this.portable ? void 0 : readEntry.atime; - this.ctime = this.portable ? void 0 : readEntry.ctime; - this.linkpath = readEntry.linkpath !== void 0 ? normalizeWindowsPath(readEntry.linkpath) : void 0; - if (typeof opt.onwarn === "function") { - this.on("warn", opt.onwarn); + for (var i = 0; !!walker; i++) { + acc = fn2(acc, walker.value, i); + walker = walker.next; } - let pathWarn = false; - if (!this.preservePaths) { - const [root, stripped] = stripAbsolutePath(this.path); - if (root && typeof stripped === "string") { - this.path = stripped; - pathWarn = root; - } + return acc; + } + reduceReverse(fn2, initial) { + let acc; + let walker = this.tail; + if (arguments.length > 1) { + acc = initial; + } else if (this.tail) { + walker = this.tail.prev; + acc = this.tail.value; + } else { + throw new TypeError("Reduce of empty list with no initial value"); } - this.remain = readEntry.size; - this.blockRemain = readEntry.startBlockSize; - this.onWriteEntry?.(this); - this.header = new Header({ - path: this[PREFIX](this.path), - linkpath: this.type === "Link" && this.linkpath !== void 0 ? this[PREFIX](this.linkpath) : this.linkpath, - // only the permissions and setuid/setgid/sticky bitflags - // not the higher-order bits that specify file type - mode: this.mode, - uid: this.portable ? void 0 : this.uid, - gid: this.portable ? void 0 : this.gid, - size: this.size, - mtime: this.noMtime ? void 0 : this.mtime, - type: this.type, - uname: this.portable ? void 0 : this.uname, - atime: this.portable ? void 0 : this.atime, - ctime: this.portable ? void 0 : this.ctime - }); - if (pathWarn) { - this.warn("TAR_ENTRY_INFO", `stripping ${pathWarn} from absolute path`, { - entry: this, - path: pathWarn + this.path - }); + for (let i = this.length - 1; !!walker; i--) { + acc = fn2(acc, walker.value, i); + walker = walker.prev; } - if (this.header.encode() && !this.noPax) { - super.write(new Pax({ - atime: this.portable ? void 0 : this.atime, - ctime: this.portable ? void 0 : this.ctime, - gid: this.portable ? void 0 : this.gid, - mtime: this.noMtime ? void 0 : this.mtime, - path: this[PREFIX](this.path), - linkpath: this.type === "Link" && this.linkpath !== void 0 ? this[PREFIX](this.linkpath) : this.linkpath, - size: this.size, - uid: this.portable ? void 0 : this.uid, - uname: this.portable ? void 0 : this.uname, - dev: this.portable ? void 0 : this.readEntry.dev, - ino: this.portable ? void 0 : this.readEntry.ino, - nlink: this.portable ? void 0 : this.readEntry.nlink - }).encode()); + return acc; + } + toArray() { + const arr = new Array(this.length); + for (let i = 0, walker = this.head; !!walker; i++) { + arr[i] = walker.value; + walker = walker.next; } - const b = this.header?.block; - if (!b) - throw new Error("failed to encode header"); - super.write(b); - readEntry.pipe(this); + return arr; } - [PREFIX](path16) { - return prefixPath(path16, this.prefix); + toArrayReverse() { + const arr = new Array(this.length); + for (let i = 0, walker = this.tail; !!walker; i++) { + arr[i] = walker.value; + walker = walker.prev; + } + return arr; } - [MODE](mode) { - return modeFix(mode, this.type === "Directory", this.portable); + slice(from = 0, to = this.length) { + if (to < 0) { + to += this.length; + } + if (from < 0) { + from += this.length; + } + const ret = new _Yallist(); + if (to < from || to < 0) { + return ret; + } + if (from < 0) { + from = 0; + } + if (to > this.length) { + to = this.length; + } + let walker = this.head; + let i = 0; + for (i = 0; !!walker && i < from; i++) { + walker = walker.next; + } + for (; !!walker && i < to; i++, walker = walker.next) { + ret.push(walker.value); + } + return ret; } - write(chunk, encoding, cb) { - if (typeof encoding === "function") { - cb = encoding; - encoding = void 0; + sliceReverse(from = 0, to = this.length) { + if (to < 0) { + to += this.length; } - if (typeof chunk === "string") { - chunk = Buffer.from(chunk, typeof encoding === "string" ? encoding : "utf8"); + if (from < 0) { + from += this.length; } - const writeLen = chunk.length; - if (writeLen > this.blockRemain) { - throw new Error("writing more to entry than is appropriate"); + const ret = new _Yallist(); + if (to < from || to < 0) { + return ret; } - this.blockRemain -= writeLen; - return super.write(chunk, cb); + if (from < 0) { + from = 0; + } + if (to > this.length) { + to = this.length; + } + let i = this.length; + let walker = this.tail; + for (; !!walker && i > to; i--) { + walker = walker.prev; + } + for (; !!walker && i > from; i--, walker = walker.prev) { + ret.push(walker.value); + } + return ret; } - end(chunk, encoding, cb) { - if (this.blockRemain) { - super.write(Buffer.alloc(this.blockRemain)); + splice(start, deleteCount = 0, ...nodes) { + if (start > this.length) { + start = this.length - 1; } - if (typeof chunk === "function") { - cb = chunk; - encoding = void 0; - chunk = void 0; + if (start < 0) { + start = this.length + start; } - if (typeof encoding === "function") { - cb = encoding; - encoding = void 0; + let walker = this.head; + for (let i = 0; !!walker && i < start; i++) { + walker = walker.next; } - if (typeof chunk === "string") { - chunk = Buffer.from(chunk, encoding ?? "utf8"); + const ret = []; + for (let i = 0; !!walker && i < deleteCount; i++) { + ret.push(walker.value); + walker = this.removeNode(walker); } - if (cb) - this.once("finish", cb); - chunk ? super.end(chunk, cb) : super.end(cb); + if (!walker) { + walker = this.tail; + } else if (walker !== this.tail) { + walker = walker.prev; + } + for (const v of nodes) { + walker = insertAfter(this, walker, v); + } + return ret; + } + reverse() { + const head = this.head; + const tail = this.tail; + for (let walker = head; !!walker; walker = walker.prev) { + const p = walker.prev; + walker.prev = walker.next; + walker.next = p; + } + this.head = tail; + this.tail = head; return this; } }; - getType = (stat2) => stat2.isFile() ? "File" : stat2.isDirectory() ? "Directory" : stat2.isSymbolicLink() ? "SymbolicLink" : "Unsupported"; + Node = class { + list; + next; + prev; + value; + constructor(value, prev, next, list2) { + this.list = list2; + this.value = value; + if (prev) { + prev.next = this; + this.prev = prev; + } else { + this.prev = void 0; + } + if (next) { + next.prev = this; + this.next = next; + } else { + this.next = void 0; + } + } + }; } }); -// .yarn/cache/tar-npm-7.4.3-1dbbd1ffc3-d4679609bb.zip/node_modules/tar/dist/esm/pack.js -var import_fs15, import_path14, PackJob, EOF2, ONSTAT, ENDED3, QUEUE2, CURRENT, PROCESS2, PROCESSING, PROCESSJOB, JOBS, JOBDONE, ADDFSENTRY, ADDTARENTRY, STAT, READDIR, ONREADDIR, PIPE, ENTRY, ENTRYOPT, WRITEENTRYCLASS, WRITE, ONDRAIN2, Pack, PackSync; +// .yarn/cache/tar-npm-7.5.1-7b414f7fec-0dad0596a6.zip/node_modules/tar/dist/esm/pack.js +var import_fs12, import_path10, PackJob, EOF2, ONSTAT, ENDED3, QUEUE2, CURRENT, PROCESS2, PROCESSING, PROCESSJOB, JOBS, JOBDONE, ADDFSENTRY, ADDTARENTRY, STAT, READDIR, ONREADDIR, PIPE, ENTRY, ENTRYOPT, WRITEENTRYCLASS, WRITE, ONDRAIN2, Pack, PackSync; var init_pack = __esm({ - ".yarn/cache/tar-npm-7.4.3-1dbbd1ffc3-d4679609bb.zip/node_modules/tar/dist/esm/pack.js"() { - import_fs15 = __toESM(require("fs"), 1); + ".yarn/cache/tar-npm-7.5.1-7b414f7fec-0dad0596a6.zip/node_modules/tar/dist/esm/pack.js"() { + import_fs12 = __toESM(require("fs"), 1); init_write_entry(); init_esm(); init_esm3(); - init_esm4(); + init_esm5(); init_read_entry(); init_warn_method(); - import_path14 = __toESM(require("path"), 1); + import_path10 = __toESM(require("path"), 1); init_normalize_windows_path(); PackJob = class { path; @@ -19579,6 +19556,14 @@ var init_pack = __esm({ jobs; [WRITEENTRYCLASS]; onWriteEntry; + // Note: we actually DO need a linked list here, because we + // shift() to update the head of the list where we start, but still + // while that happens, need to know what the next item in the queue + // will be. Since we do multiple jobs in parallel, it's not as simple + // as just an Array.shift(), since that would lose the information about + // the next job in the list. We could add a .next field on the PackJob + // class, but then we'd have to be tracking the tail of the queue the + // whole time, and Yallist just does that for us anyway. [QUEUE2]; [JOBS] = 0; [PROCESSING] = false; @@ -19602,9 +19587,9 @@ var init_pack = __esm({ this.on("warn", opt.onwarn); } this.portable = !!opt.portable; - if (opt.gzip || opt.brotli) { - if (opt.gzip && opt.brotli) { - throw new TypeError("gzip and brotli are mutually exclusive"); + if (opt.gzip || opt.brotli || opt.zstd) { + if ((opt.gzip ? 1 : 0) + (opt.brotli ? 1 : 0) + (opt.zstd ? 1 : 0) > 1) { + throw new TypeError("gzip, brotli, zstd are mutually exclusive"); } if (opt.gzip) { if (typeof opt.gzip !== "object") { @@ -19621,6 +19606,12 @@ var init_pack = __esm({ } this.zip = new BrotliCompress(opt.brotli); } + if (opt.zstd) { + if (typeof opt.zstd !== "object") { + opt.zstd = {}; + } + this.zip = new ZstdCompress(opt.zstd); + } if (!this.zip) throw new Error("impossible"); const zip = this.zip; @@ -19680,7 +19671,7 @@ var init_pack = __esm({ return this.flowing; } [ADDTARENTRY](p) { - const absolute = normalizeWindowsPath(import_path14.default.resolve(this.cwd, p.path)); + const absolute = normalizeWindowsPath(import_path10.default.resolve(this.cwd, p.path)); if (!this.filter(p.path, p)) { p.resume(); } else { @@ -19693,28 +19684,28 @@ var init_pack = __esm({ this[PROCESS2](); } [ADDFSENTRY](p) { - const absolute = normalizeWindowsPath(import_path14.default.resolve(this.cwd, p)); + const absolute = normalizeWindowsPath(import_path10.default.resolve(this.cwd, p)); this[QUEUE2].push(new PackJob(p, absolute)); this[PROCESS2](); } [STAT](job) { job.pending = true; this[JOBS] += 1; - const stat2 = this.follow ? "stat" : "lstat"; - import_fs15.default[stat2](job.absolute, (er, stat3) => { + const stat = this.follow ? "stat" : "lstat"; + import_fs12.default[stat](job.absolute, (er, stat2) => { job.pending = false; this[JOBS] -= 1; if (er) { this.emit("error", er); } else { - this[ONSTAT](job, stat3); + this[ONSTAT](job, stat2); } }); } - [ONSTAT](job, stat2) { - this.statCache.set(job.absolute, stat2); - job.stat = stat2; - if (!this.filter(job.path, stat2)) { + [ONSTAT](job, stat) { + this.statCache.set(job.absolute, stat); + job.stat = stat; + if (!this.filter(job.path, stat)) { job.ignore = true; } this[PROCESS2](); @@ -19722,7 +19713,7 @@ var init_pack = __esm({ [READDIR](job) { job.pending = true; this[JOBS] += 1; - import_fs15.default.readdir(job.absolute, (er, entries) => { + import_fs12.default.readdir(job.absolute, (er, entries) => { job.pending = false; this[JOBS] -= 1; if (er) { @@ -19893,11 +19884,11 @@ var init_pack = __esm({ resume() { } [STAT](job) { - const stat2 = this.follow ? "statSync" : "lstatSync"; - this[ONSTAT](job, import_fs15.default[stat2](job.absolute)); + const stat = this.follow ? "statSync" : "lstatSync"; + this[ONSTAT](job, import_fs12.default[stat](job.absolute)); } [READDIR](job) { - this[ONREADDIR](job, import_fs15.default.readdirSync(job.absolute)); + this[ONREADDIR](job, import_fs12.default.readdirSync(job.absolute)); } // gotta get it all in this tick [PIPE](job) { @@ -19926,14 +19917,14 @@ var init_pack = __esm({ } }); -// .yarn/cache/tar-npm-7.4.3-1dbbd1ffc3-d4679609bb.zip/node_modules/tar/dist/esm/create.js +// .yarn/cache/tar-npm-7.5.1-7b414f7fec-0dad0596a6.zip/node_modules/tar/dist/esm/create.js var create_exports = {}; __export(create_exports, { create: () => create }); var import_node_path8, createFileSync, createFile, addFilesSync, addFilesAsync, createSync, createAsync, create; var init_create = __esm({ - ".yarn/cache/tar-npm-7.4.3-1dbbd1ffc3-d4679609bb.zip/node_modules/tar/dist/esm/create.js"() { + ".yarn/cache/tar-npm-7.5.1-7b414f7fec-0dad0596a6.zip/node_modules/tar/dist/esm/create.js"() { init_esm2(); import_node_path8 = __toESM(require("node:path"), 1); init_list(); @@ -20011,9 +20002,10 @@ var init_create = __esm({ } }); -// .yarn/cache/semver-npm-7.7.1-4572475307-fd603a6fb9.zip/node_modules/semver/functions/major.js +// .yarn/cache/semver-npm-7.7.3-9cf7b3b46c-4afe5c9865.zip/node_modules/semver/functions/major.js var require_major = __commonJS({ - ".yarn/cache/semver-npm-7.7.1-4572475307-fd603a6fb9.zip/node_modules/semver/functions/major.js"(exports2, module2) { + ".yarn/cache/semver-npm-7.7.3-9cf7b3b46c-4afe5c9865.zip/node_modules/semver/functions/major.js"(exports2, module2) { + "use strict"; var SemVer3 = require_semver(); var major = (a, loose) => new SemVer3(a, loose).major; module2.exports = major; @@ -21683,11 +21675,11 @@ function String2(descriptor, ...args) { } // package.json -var version = "0.34.0"; +var version = "0.34.1"; // sources/Engine.ts -var import_fs9 = __toESM(require("fs")); -var import_path9 = __toESM(require("path")); +var import_fs6 = __toESM(require("fs")); +var import_path5 = __toESM(require("path")); var import_process3 = __toESM(require("process")); var import_rcompare = __toESM(require_rcompare()); var import_valid3 = __toESM(require_valid()); @@ -21697,7 +21689,7 @@ var import_valid4 = __toESM(require_valid2()); var config_default = { definitions: { npm: { - default: "11.4.2+sha1.6f1519a03f7e04023a957a22b812832d0c4a4b33", + default: "11.6.2+sha1.2af8ff1f23b279df1e5289db7c70cfedd0fe18c5", fetchLatestFrom: { type: "npm", package: "npm" @@ -21734,7 +21726,7 @@ var config_default = { } }, pnpm: { - default: "10.13.1+sha1.aa8c167c4509c97519542ef77a09e4b8ab59fb6a", + default: "10.18.3+sha1.0202a20aaa3d7ba8bc29b50d95efe1a34dd95773", fetchLatestFrom: { type: "npm", package: "pnpm" @@ -21798,7 +21790,7 @@ var config_default = { package: "yarn" }, transparent: { - default: "4.9.2+sha224.b8e0b161ae590950fbda696e6f3ca071362768e5280c5fbfdadf064b", + default: "4.10.3+sha224.6020b3cdcdfbd7dbc24b7a7b75d58a249ce36068a8bf97d39aa8cc6d", commands: [ [ "yarn", @@ -21881,14 +21873,14 @@ var config_default = { // sources/corepackUtils.ts var import_crypto2 = require("crypto"); var import_events4 = require("events"); -var import_fs7 = __toESM(require("fs")); +var import_fs4 = __toESM(require("fs")); var import_module = __toESM(require("module")); -var import_path7 = __toESM(require("path")); +var import_path3 = __toESM(require("path")); var import_range = __toESM(require_range()); var import_semver = __toESM(require_semver()); var import_lt = __toESM(require_lt()); var import_parse3 = __toESM(require_parse()); -var import_promises = require("timers/promises"); +var import_promises2 = require("timers/promises"); // sources/debugUtils.ts var import_debug = __toESM(require_src()); @@ -21945,7 +21937,7 @@ var DEFAULT_HEADERS = { [`Accept`]: `application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8` }; var DEFAULT_NPM_REGISTRY_URL = `https://registry.npmjs.org`; -async function fetchAsJson2(packageName, version3) { +async function fetchAsJson2(packageName, version2) { const npmRegistryUrl = process.env.COREPACK_NPM_REGISTRY || DEFAULT_NPM_REGISTRY_URL; if (process.env.COREPACK_ENABLE_NETWORK === `0`) throw new UsageError(`Network access disabled by the environment; can't reach npm repository ${npmRegistryUrl}`); @@ -21956,9 +21948,9 @@ async function fetchAsJson2(packageName, version3) { const encodedCreds = Buffer.from(`${process.env.COREPACK_NPM_USERNAME}:${process.env.COREPACK_NPM_PASSWORD}`, `utf8`).toString(`base64`); headers.authorization = `Basic ${encodedCreds}`; } - return fetchAsJson(`${npmRegistryUrl}/${packageName}${version3 ? `/${version3}` : ``}`, { headers }); + return fetchAsJson(`${npmRegistryUrl}/${packageName}${version2 ? `/${version2}` : ``}`, { headers }); } -function verifySignature({ signatures, integrity, packageName, version: version3 }) { +function verifySignature({ signatures, integrity, packageName, version: version2 }) { if (!Array.isArray(signatures) || !signatures.length) throw new Error(`No compatible signature found in package metadata`); const { npm: trustedKeys } = process.env.COREPACK_INTEGRITY_KEYS ? JSON.parse(process.env.COREPACK_INTEGRITY_KEYS) : config_default.keys; let signature; @@ -21972,7 +21964,7 @@ function verifySignature({ signatures, integrity, packageName, version: version3 } if (signature?.sig == null) throw new UsageError(`The package was not signed by any trusted keys: ${JSON.stringify({ signatures, trustedKeys }, void 0, 2)}`); const verifier = (0, import_crypto.createVerify)(`SHA256`); - verifier.end(`${packageName}@${version3}:${integrity}`); + verifier.end(`${packageName}@${version2}:${integrity}`); const valid = verifier.verify( `-----BEGIN PUBLIC KEY----- ${key} @@ -21986,12 +21978,12 @@ ${key} } async function fetchLatestStableVersion(packageName) { const metadata = await fetchAsJson2(packageName, `latest`); - const { version: version3, dist: { integrity, signatures, shasum } } = metadata; + const { version: version2, dist: { integrity, signatures, shasum } } = metadata; if (!shouldSkipIntegrityCheck()) { try { verifySignature({ packageName, - version: version3, + version: version2, integrity, signatures }); @@ -21999,7 +21991,7 @@ async function fetchLatestStableVersion(packageName) { throw new Error(`Corepack cannot download the latest stable version of ${packageName}; you can disable signature verification by setting COREPACK_INTEGRITY_CHECK to 0 in your env, or instruct Corepack to use the latest stable release known by this version of Corepack by setting COREPACK_USE_LATEST to 0`, { cause }); } } - return `${version3}+${integrity ? `sha512.${Buffer.from(integrity.slice(7), `base64`).toString(`hex`)}` : `sha1.${shasum}`}`; + return `${version2}+${integrity ? `sha512.${Buffer.from(integrity.slice(7), `base64`).toString(`hex`)}` : `sha1.${shasum}`}`; } async function fetchAvailableTags(packageName) { const metadata = await fetchAsJson2(packageName); @@ -22009,11 +22001,11 @@ async function fetchAvailableVersions(packageName) { const metadata = await fetchAsJson2(packageName); return Object.keys(metadata.versions); } -async function fetchTarballURLAndSignature(packageName, version3) { - const versionMetadata = await fetchAsJson2(packageName, version3); +async function fetchTarballURLAndSignature(packageName, version2) { + const versionMetadata = await fetchAsJson2(packageName, version2); const { tarball, signatures, integrity } = versionMetadata.dist; if (tarball === void 0 || !tarball.startsWith(`http`)) - throw new Error(`${packageName}@${version3} does not have a valid tarball.`); + throw new Error(`${packageName}@${version2} does not have a valid tarball.`); return { tarball, signatures, integrity }; } @@ -22034,7 +22026,8 @@ async function fetch(input, init) { }; input.username = input.password = ``; } - if (input.origin === (process.env.COREPACK_NPM_REGISTRY || DEFAULT_NPM_REGISTRY_URL) && process.env.COREPACK_NPM_TOKEN) { + const registry = process.env.COREPACK_NPM_TOKEN && new URL(process.env.COREPACK_NPM_REGISTRY || DEFAULT_NPM_REGISTRY_URL); + if (registry && input.origin === registry.origin) { headers = { ...headers, authorization: `Bearer ${process.env.COREPACK_NPM_TOKEN}` @@ -22156,10 +22149,10 @@ async function fetchAvailableVersions2(spec) { } } async function findInstalledVersion(installTarget, descriptor) { - const installFolder = import_path7.default.join(installTarget, descriptor.name); + const installFolder = import_path3.default.join(installTarget, descriptor.name); let cacheDirectory; try { - cacheDirectory = await import_fs7.default.promises.opendir(installFolder); + cacheDirectory = await import_fs4.default.promises.opendir(installFolder); } catch (error) { if (error.code === `ENOENT`) { return null; @@ -22207,7 +22200,7 @@ async function download(installTarget, url, algo, binPath = null) { log(`Downloading to ${tmpFolder}`); const stream = await fetchUrlStream(url); const parsedUrl = new URL(url); - const ext = import_path7.default.posix.extname(parsedUrl.pathname); + const ext = import_path3.default.posix.extname(parsedUrl.pathname); let outputFile = null; let sendTo; if (ext === `.tgz`) { @@ -22221,15 +22214,15 @@ async function download(installTarget, url, algo, binPath = null) { } : void 0 }); } else if (ext === `.js`) { - outputFile = import_path7.default.join(tmpFolder, import_path7.default.posix.basename(parsedUrl.pathname)); - sendTo = import_fs7.default.createWriteStream(outputFile); + outputFile = import_path3.default.join(tmpFolder, import_path3.default.posix.basename(parsedUrl.pathname)); + sendTo = import_fs4.default.createWriteStream(outputFile); } stream.pipe(sendTo); let hash = !binPath ? stream.pipe((0, import_crypto2.createHash)(algo)) : null; await (0, import_events4.once)(sendTo, `finish`); if (binPath) { - const downloadedBin = import_path7.default.join(tmpFolder, binPath); - outputFile = import_path7.default.join(tmpFolder, import_path7.default.basename(downloadedBin)); + const downloadedBin = import_path3.default.join(tmpFolder, binPath); + outputFile = import_path3.default.join(tmpFolder, import_path3.default.basename(downloadedBin)); try { await renameSafe(downloadedBin, outputFile); } catch (err) { @@ -22237,7 +22230,7 @@ async function download(installTarget, url, algo, binPath = null) { throw new Error(`Cannot locate '${binPath}' in downloaded tarball`, { cause: err }); throw err; } - const fileStream = import_fs7.default.createReadStream(outputFile); + const fileStream = import_fs4.default.createReadStream(outputFile); hash = fileStream.pipe((0, import_crypto2.createHash)(algo)); await (0, import_events4.once)(fileStream, `close`); } @@ -22250,11 +22243,11 @@ async function download(installTarget, url, algo, binPath = null) { async function installVersion(installTarget, locator, { spec }) { const locatorIsASupportedPackageManager = isSupportedPackageManagerLocator(locator); const locatorReference = locatorIsASupportedPackageManager ? (0, import_parse3.default)(locator.reference) : parseURLReference(locator); - const { version: version3, build } = locatorReference; - const installFolder = import_path7.default.join(installTarget, locator.name, version3); + const { version: version2, build } = locatorReference; + const installFolder = import_path3.default.join(installTarget, locator.name, version2); try { - const corepackFile = import_path7.default.join(installFolder, `.corepack`); - const corepackContent = await import_fs7.default.promises.readFile(corepackFile, `utf8`); + const corepackFile = import_path3.default.join(installFolder, `.corepack`); + const corepackContent = await import_fs4.default.promises.readFile(corepackFile, `utf8`); const corepackData = JSON.parse(corepackContent); log(`Reusing ${locator.name}@${locator.reference} found in ${installFolder}`); return { @@ -22272,11 +22265,11 @@ async function installVersion(installTarget, locator, { spec }) { let integrity; let binPath = null; if (locatorIsASupportedPackageManager) { - url = spec.url.replace(`{}`, version3); + url = spec.url.replace(`{}`, version2); if (process.env.COREPACK_NPM_REGISTRY) { const registry = getRegistryFromPackageManagerSpec(spec); if (registry.type === `npm`) { - ({ tarball: url, signatures, integrity } = await fetchTarballURLAndSignature(registry.package, version3)); + ({ tarball: url, signatures, integrity } = await fetchTarballURLAndSignature(registry.package, version2)); if (registry.bin) { binPath = registry.bin; } @@ -22287,7 +22280,7 @@ async function installVersion(installTarget, locator, { spec }) { ); } } else { - url = decodeURIComponent(version3); + url = decodeURIComponent(version2); if (process.env.COREPACK_NPM_REGISTRY && url.startsWith(DEFAULT_NPM_REGISTRY_URL)) { url = url.replace( DEFAULT_NPM_REGISTRY_URL, @@ -22295,7 +22288,7 @@ async function installVersion(installTarget, locator, { spec }) { ); } } - log(`Installing ${locator.name}@${version3} from ${url}`); + log(`Installing ${locator.name}@${version2} from ${url}`); const algo = build[0] ?? `sha512`; const { tmpFolder, outputFile, hash: actualHash } = await download(installTarget, url, algo, binPath); let bin; @@ -22310,7 +22303,7 @@ async function installVersion(installTarget, locator, { spec }) { if (locatorIsASupportedPackageManager && isValidBinSpec(spec.bin)) { bin = spec.bin; } else { - const { name: packageName, bin: packageBin } = require(import_path7.default.join(tmpFolder, `package.json`)); + const { name: packageName, bin: packageBin } = require(import_path3.default.join(tmpFolder, `package.json`)); if (typeof packageBin === `string`) { bin = { [packageName]: packageBin }; } else if (isValidBinSpec(packageBin)) { @@ -22324,27 +22317,27 @@ async function installVersion(installTarget, locator, { spec }) { const registry = getRegistryFromPackageManagerSpec(spec); if (registry.type === `npm` && !registry.bin && !shouldSkipIntegrityCheck()) { if (signatures == null || integrity == null) - ({ signatures, integrity } = await fetchTarballURLAndSignature(registry.package, version3)); - verifySignature({ signatures, integrity, packageName: registry.package, version: version3 }); + ({ signatures, integrity } = await fetchTarballURLAndSignature(registry.package, version2)); + verifySignature({ signatures, integrity, packageName: registry.package, version: version2 }); build[1] = Buffer.from(integrity.slice(`sha512-`.length), `base64`).toString(`hex`); } } if (build[1] && actualHash !== build[1]) throw new Error(`Mismatch hashes. Expected ${build[1]}, got ${actualHash}`); const serializedHash = `${algo}.${actualHash}`; - await import_fs7.default.promises.writeFile(import_path7.default.join(tmpFolder, `.corepack`), JSON.stringify({ + await import_fs4.default.promises.writeFile(import_path3.default.join(tmpFolder, `.corepack`), JSON.stringify({ locator, bin, hash: serializedHash })); - await import_fs7.default.promises.mkdir(import_path7.default.dirname(installFolder), { recursive: true }); + await import_fs4.default.promises.mkdir(import_path3.default.dirname(installFolder), { recursive: true }); try { await renameSafe(tmpFolder, installFolder); } catch (err) { if (err.code === `ENOTEMPTY` || // On Windows the error code is EPERM so we check if it is a directory - err.code === `EPERM` && (await import_fs7.default.promises.stat(installFolder)).isDirectory()) { + err.code === `EPERM` && (await import_fs4.default.promises.stat(installFolder)).isDirectory()) { log(`Another instance of corepack installed ${locator.name}@${locator.reference}`); - await import_fs7.default.promises.rm(tmpFolder, { recursive: true, force: true }); + await import_fs4.default.promises.rm(tmpFolder, { recursive: true, force: true }); } else { throw err; } @@ -22371,18 +22364,18 @@ async function renameSafe(oldPath, newPath) { if (process.platform === `win32`) { await renameUnderWindows(oldPath, newPath); } else { - await import_fs7.default.promises.rename(oldPath, newPath); + await import_fs4.default.promises.rename(oldPath, newPath); } } async function renameUnderWindows(oldPath, newPath) { const retries = 5; for (let i = 0; i < retries; i++) { try { - await import_fs7.default.promises.rename(oldPath, newPath); + await import_fs4.default.promises.rename(oldPath, newPath); break; } catch (err) { if ((err.code === `ENOENT` || err.code === `EPERM`) && i < retries - 1) { - await (0, import_promises.setTimeout)(100 * 2 ** i); + await (0, import_promises2.setTimeout)(100 * 2 ** i); continue; } else { throw err; @@ -22396,15 +22389,15 @@ async function runVersion(locator, installSpec, binName, args) { if (Array.isArray(bin)) { if (bin.some((name2) => name2 === binName)) { const parsedUrl = new URL(installSpec.spec.url); - const ext = import_path7.default.posix.extname(parsedUrl.pathname); + const ext = import_path3.default.posix.extname(parsedUrl.pathname); if (ext === `.js`) { - binPath = import_path7.default.join(installSpec.location, import_path7.default.posix.basename(parsedUrl.pathname)); + binPath = import_path3.default.join(installSpec.location, import_path3.default.posix.basename(parsedUrl.pathname)); } } } else { for (const [name2, dest] of Object.entries(bin)) { if (name2 === binName) { - binPath = import_path7.default.join(installSpec.location, dest); + binPath = import_path3.default.join(installSpec.location, dest); break; } } @@ -22416,7 +22409,7 @@ async function runVersion(locator, installSpec, binName, args) { await Promise.resolve().then(() => __toESM(require_v8_compile_cache())); } } - process.env.COREPACK_ROOT = import_path7.default.dirname(require.resolve("corepack/package.json")); + process.env.COREPACK_ROOT = import_path3.default.dirname(require.resolve("corepack/package.json")); process.argv = [ process.execPath, binPath, @@ -22436,18 +22429,18 @@ function shouldSkipIntegrityCheck() { // sources/semverUtils.ts var import_range2 = __toESM(require_range()); var import_semver2 = __toESM(require_semver()); -function satisfiesWithPrereleases(version3, range, loose = false) { +function satisfiesWithPrereleases(version2, range, loose = false) { let semverRange; try { semverRange = new import_range2.default(range, loose); } catch (err) { return false; } - if (!version3) + if (!version2) return false; let semverVersion; try { - semverVersion = new import_semver2.default(version3, semverRange.loose); + semverVersion = new import_semver2.default(version2, semverRange.loose); if (semverVersion.prerelease) { semverVersion.prerelease = []; } @@ -22465,8 +22458,8 @@ function satisfiesWithPrereleases(version3, range, loose = false) { } // sources/specUtils.ts -var import_fs8 = __toESM(require("fs")); -var import_path8 = __toESM(require("path")); +var import_fs5 = __toESM(require("fs")); +var import_path4 = __toESM(require("path")); var import_satisfies = __toESM(require_satisfies()); var import_valid = __toESM(require_valid()); var import_valid2 = __toESM(require_valid2()); @@ -22585,24 +22578,24 @@ function parsePackageJSON(packageJSONContent) { console.warn(`! Corepack does not currently support array values for devEngines.packageManager`); return pm; } - const { name: name2, version: version3, onFail } = packageManager; + const { name: name2, version: version2, onFail } = packageManager; if (typeof name2 !== `string` || name2.includes(`@`)) { warnOrThrow(`The value of devEngines.packageManager.name ${JSON.stringify(name2)} is not a supported string value`, onFail); return pm; } - if (version3 != null && (typeof version3 !== `string` || !(0, import_valid2.default)(version3))) { - warnOrThrow(`The value of devEngines.packageManager.version ${JSON.stringify(version3)} is not a valid semver range`, onFail); + if (version2 != null && (typeof version2 !== `string` || !(0, import_valid2.default)(version2))) { + warnOrThrow(`The value of devEngines.packageManager.version ${JSON.stringify(version2)} is not a valid semver range`, onFail); return pm; } - log(`devEngines.packageManager defines that ${name2}@${version3} is the local package manager`); + log(`devEngines.packageManager defines that ${name2}@${version2} is the local package manager`); if (pm) { if (!pm.startsWith?.(`${name2}@`)) warnOrThrow(`"packageManager" field is set to ${JSON.stringify(pm)} which does not match the "devEngines.packageManager" field set to ${JSON.stringify(name2)}`, onFail); - else if (version3 != null && !(0, import_satisfies.default)(pm.slice(packageManager.name.length + 1), version3)) - warnOrThrow(`"packageManager" field is set to ${JSON.stringify(pm)} which does not match the value defined in "devEngines.packageManager" for ${JSON.stringify(name2)} of ${JSON.stringify(version3)}`, onFail); + else if (version2 != null && !(0, import_satisfies.default)(pm.slice(packageManager.name.length + 1), version2)) + warnOrThrow(`"packageManager" field is set to ${JSON.stringify(pm)} which does not match the value defined in "devEngines.packageManager" for ${JSON.stringify(name2)} of ${JSON.stringify(version2)}`, onFail); return pm; } - return `${name2}@${version3 ?? `*`}`; + return `${name2}@${version2 ?? `*`}`; } return pm; } @@ -22614,13 +22607,13 @@ async function setLocalPackageManager(cwd, info) { warnOrThrow(`The requested version of ${info.locator.name}@${info.locator.reference} does not match the devEngines specification (${range.name}@${range.range})`, range.onFail); } } - const content = lookup.type !== `NoProject` ? await import_fs8.default.promises.readFile(lookup.target, `utf8`) : ``; + const content = lookup.type !== `NoProject` ? await import_fs5.default.promises.readFile(lookup.target, `utf8`) : ``; const { data, indent } = readPackageJson(content); const previousPackageManager = data.packageManager ?? (range ? `${range.name}@${range.range}` : `unknown`); data.packageManager = `${info.locator.name}@${info.locator.reference}`; const newContent = normalizeLineEndings(content, `${JSON.stringify(data, null, indent)} `); - await import_fs8.default.promises.writeFile(lookup.target, newContent, `utf8`); + await import_fs5.default.promises.writeFile(lookup.target, newContent, `utf8`); return { previousPackageManager }; @@ -22631,14 +22624,14 @@ async function loadSpec(initialCwd) { let selection = null; while (nextCwd !== currCwd && (!selection || !selection.data.packageManager)) { currCwd = nextCwd; - nextCwd = import_path8.default.dirname(currCwd); + nextCwd = import_path4.default.dirname(currCwd); if (nodeModulesRegExp.test(currCwd)) continue; - const manifestPath = import_path8.default.join(currCwd, `package.json`); + const manifestPath = import_path4.default.join(currCwd, `package.json`); log(`Checking ${manifestPath}`); let content; try { - content = await import_fs8.default.promises.readFile(manifestPath, `utf8`); + content = await import_fs5.default.promises.readFile(manifestPath, `utf8`); } catch (err) { if (err?.code === `ENOENT`) continue; throw err; @@ -22649,9 +22642,9 @@ async function loadSpec(initialCwd) { } catch { } if (typeof data !== `object` || data === null) - throw new UsageError(`Invalid package.json in ${import_path8.default.relative(initialCwd, manifestPath)}`); + throw new UsageError(`Invalid package.json in ${import_path4.default.relative(initialCwd, manifestPath)}`); let localEnv; - const envFilePath2 = import_path8.default.resolve(currCwd, process.env.COREPACK_ENV_FILE ?? `.corepack.env`); + const envFilePath2 = import_path4.default.resolve(currCwd, process.env.COREPACK_ENV_FILE ?? `.corepack.env`); if (process.env.COREPACK_ENV_FILE == `0`) { log(`Skipping env file as configured with COREPACK_ENV_FILE`); localEnv = process.env; @@ -22662,7 +22655,7 @@ async function loadSpec(initialCwd) { log(`Checking ${envFilePath2}`); try { localEnv = { - ...Object.fromEntries(Object.entries((0, import_util.parseEnv)(await import_fs8.default.promises.readFile(envFilePath2, `utf8`))).filter((e) => e[0].startsWith(`COREPACK_`))), + ...Object.fromEntries(Object.entries((0, import_util.parseEnv)(await import_fs5.default.promises.readFile(envFilePath2, `utf8`))).filter((e) => e[0].startsWith(`COREPACK_`))), ...process.env }; log(`Successfully loaded env file found at ${envFilePath2}`); @@ -22676,7 +22669,7 @@ async function loadSpec(initialCwd) { selection = { data, manifestPath, localEnv, envFilePath: envFilePath2 }; } if (selection === null) - return { type: `NoProject`, target: import_path8.default.join(initialCwd, `package.json`) }; + return { type: `NoProject`, target: import_path4.default.join(initialCwd, `package.json`) }; let envFilePath; if (selection.localEnv !== process.env) { envFilePath = selection.envFilePath; @@ -22696,20 +22689,20 @@ async function loadSpec(initialCwd) { onFail: selection.data.devEngines.packageManager.onFail }, // Lazy-loading it so we do not throw errors on commands that do not need valid spec. - getSpec: () => parseSpec(rawPmSpec, import_path8.default.relative(initialCwd, selection.manifestPath)) + getSpec: () => parseSpec(rawPmSpec, import_path4.default.relative(initialCwd, selection.manifestPath)) }; } // sources/Engine.ts function getLastKnownGoodFilePath() { - const lkg = import_path9.default.join(getCorepackHomeFolder(), `lastKnownGood.json`); + const lkg = import_path5.default.join(getCorepackHomeFolder(), `lastKnownGood.json`); log(`LastKnownGood file would be located at ${lkg}`); return lkg; } async function getLastKnownGood() { let raw2; try { - raw2 = await import_fs9.default.promises.readFile(getLastKnownGoodFilePath(), `utf8`); + raw2 = await import_fs6.default.promises.readFile(getLastKnownGoodFilePath(), `utf8`); } catch (err) { if (err?.code === `ENOENT`) { log(`No LastKnownGood version found in Corepack home.`); @@ -22742,8 +22735,8 @@ async function getLastKnownGood() { async function createLastKnownGoodFile(lastKnownGood) { const content = `${JSON.stringify(lastKnownGood, null, 2)} `; - await import_fs9.default.promises.mkdir(getCorepackHomeFolder(), { recursive: true }); - await import_fs9.default.promises.writeFile(getLastKnownGoodFilePath(), content, `utf8`); + await import_fs6.default.promises.mkdir(getCorepackHomeFolder(), { recursive: true }); + await import_fs6.default.promises.writeFile(getLastKnownGoodFilePath(), content, `utf8`); } function getLastKnownGoodFromFileContent(lastKnownGood, packageManager) { if (Object.hasOwn(lastKnownGood, packageManager)) @@ -22908,7 +22901,7 @@ var Engine = class { console.error(`! The local project doesn't define a 'packageManager' field. Corepack will now add one referencing ${installSpec.locator.name}@${installSpec.locator.reference}.`); console.error(`! For more details about this field, consult the documentation at https://nodejs.org/api/packages.html#packagemanager`); console.error(); - await setLocalPackageManager(import_path9.default.dirname(result.target), installSpec); + await setLocalPackageManager(import_path5.default.dirname(result.target), installSpec); } log(`Falling back to ${fallbackDescriptor.name}@${fallbackDescriptor.range} in the absence of "packageManager" field in ${result.target}`); return fallbackDescriptor; @@ -22999,7 +22992,7 @@ var Engine = class { const packageManagerSpec = definition.ranges[range]; const registry = getRegistryFromPackageManagerSpec(packageManagerSpec); const versions2 = await fetchAvailableVersions2(registry); - return versions2.filter((version3) => satisfiesWithPrereleases(version3, finalDescriptor.range)); + return versions2.filter((version2) => satisfiesWithPrereleases(version2, finalDescriptor.range)); })); const highestVersion = [...new Set(versions.flat())].sort(import_rcompare.default); if (highestVersion.length === 0) @@ -23009,7 +23002,7 @@ var Engine = class { }; // sources/commands/Cache.ts -var import_fs10 = __toESM(require("fs")); +var import_fs7 = __toESM(require("fs")); var CacheCommand = class extends Command { static paths = [ [`cache`, `clean`], @@ -23022,13 +23015,13 @@ var CacheCommand = class extends Command { ` }); async execute() { - await import_fs10.default.promises.rm(getInstallFolder(), { recursive: true, force: true }); + await import_fs7.default.promises.rm(getInstallFolder(), { recursive: true, force: true }); } }; // sources/commands/Disable.ts -var import_fs11 = __toESM(require("fs")); -var import_path10 = __toESM(require("path")); +var import_fs8 = __toESM(require("fs")); +var import_path6 = __toESM(require("path")); var import_which = __toESM(require_lib()); var DisableCommand = class extends Command { static paths = [ @@ -23059,7 +23052,7 @@ var DisableCommand = class extends Command { async execute() { let installDirectory = this.installDirectory; if (typeof installDirectory === `undefined`) - installDirectory = import_path10.default.dirname(await (0, import_which.default)(`corepack`)); + installDirectory = import_path6.default.dirname(await (0, import_which.default)(`corepack`)); const names = this.names.length === 0 ? SupportedPackageManagerSetWithoutNpm : this.names; const allBinNames = []; for (const name2 of new Set(names)) { @@ -23072,13 +23065,13 @@ var DisableCommand = class extends Command { await Promise.all(allBinNames.map(removeLink)); } async removePosixLink(installDirectory, binName) { - const file = import_path10.default.join(installDirectory, binName); + const file = import_path6.default.join(installDirectory, binName); try { - if (binName.includes(`yarn`) && isYarnSwitchPath(await import_fs11.default.promises.realpath(file))) { + if (binName.includes(`yarn`) && isYarnSwitchPath(await import_fs8.default.promises.realpath(file))) { console.warn(`${binName} is already installed in ${file} and points to a Yarn Switch install - skipping`); return; } - await import_fs11.default.promises.unlink(file); + await import_fs8.default.promises.unlink(file); } catch (err) { if (err.code !== `ENOENT`) { throw err; @@ -23087,9 +23080,9 @@ var DisableCommand = class extends Command { } async removeWin32Link(installDirectory, binName) { for (const ext of [``, `.ps1`, `.cmd`]) { - const file = import_path10.default.join(installDirectory, `${binName}${ext}`); + const file = import_path6.default.join(installDirectory, `${binName}${ext}`); try { - await import_fs11.default.promises.unlink(file); + await import_fs8.default.promises.unlink(file); } catch (err) { if (err.code !== `ENOENT`) { throw err; @@ -23101,8 +23094,8 @@ var DisableCommand = class extends Command { // sources/commands/Enable.ts var import_cmd_shim = __toESM(require_cmd_shim()); -var import_fs12 = __toESM(require("fs")); -var import_path11 = __toESM(require("path")); +var import_fs9 = __toESM(require("fs")); +var import_path7 = __toESM(require("path")); var import_which2 = __toESM(require_lib()); var EnableCommand = class extends Command { static paths = [ @@ -23133,11 +23126,11 @@ var EnableCommand = class extends Command { async execute() { let installDirectory = this.installDirectory; if (typeof installDirectory === `undefined`) - installDirectory = import_path11.default.dirname(await (0, import_which2.default)(`corepack`)); - installDirectory = import_fs12.default.realpathSync(installDirectory); + installDirectory = import_path7.default.dirname(await (0, import_which2.default)(`corepack`)); + installDirectory = import_fs9.default.realpathSync(installDirectory); const manifestPath = require.resolve("corepack/package.json"); - const distFolder = import_path11.default.join(import_path11.default.dirname(manifestPath), `dist`); - if (!import_fs12.default.existsSync(distFolder)) + const distFolder = import_path7.default.join(import_path7.default.dirname(manifestPath), `dist`); + if (!import_fs9.default.existsSync(distFolder)) throw new Error(`Assertion failed: The stub folder doesn't exist`); const names = this.names.length === 0 ? SupportedPackageManagerSetWithoutNpm : this.names; const allBinNames = []; @@ -23151,33 +23144,33 @@ var EnableCommand = class extends Command { await Promise.all(allBinNames.map(generateLink)); } async generatePosixLink(installDirectory, distFolder, binName) { - const file = import_path11.default.join(installDirectory, binName); - const symlink = import_path11.default.relative(installDirectory, import_path11.default.join(distFolder, `${binName}.js`)); - if (import_fs12.default.existsSync(file)) { - const currentSymlink = await import_fs12.default.promises.readlink(file); - if (binName.includes(`yarn`) && isYarnSwitchPath(await import_fs12.default.promises.realpath(file))) { + const file = import_path7.default.join(installDirectory, binName); + const symlink = import_path7.default.relative(installDirectory, import_path7.default.join(distFolder, `${binName}.js`)); + if (import_fs9.default.existsSync(file)) { + const currentSymlink = await import_fs9.default.promises.readlink(file); + if (binName.includes(`yarn`) && isYarnSwitchPath(await import_fs9.default.promises.realpath(file))) { console.warn(`${binName} is already installed in ${file} and points to a Yarn Switch install - skipping`); return; } if (currentSymlink !== symlink) { - await import_fs12.default.promises.unlink(file); + await import_fs9.default.promises.unlink(file); } else { return; } } - await import_fs12.default.promises.symlink(symlink, file); + await import_fs9.default.promises.symlink(symlink, file); } async generateWin32Link(installDirectory, distFolder, binName) { - const file = import_path11.default.join(installDirectory, binName); - await (0, import_cmd_shim.default)(import_path11.default.join(distFolder, `${binName}.js`), file, { + const file = import_path7.default.join(installDirectory, binName); + await (0, import_cmd_shim.default)(import_path7.default.join(distFolder, `${binName}.js`), file, { createCmdFile: true }); } }; // sources/commands/InstallGlobal.ts -var import_fs13 = __toESM(require("fs")); -var import_path12 = __toESM(require("path")); +var import_fs10 = __toESM(require("fs")); +var import_path8 = __toESM(require("path")); // sources/commands/Base.ts var BaseCommand = class extends Command { @@ -23244,7 +23237,7 @@ var InstallGlobalCommand = class extends BaseCommand { throw new UsageError(`No package managers specified`); await Promise.all(this.args.map((arg) => { if (arg.endsWith(`.tgz`)) { - return this.installFromTarball(import_path12.default.resolve(this.context.cwd, arg)); + return this.installFromTarball(import_path8.default.resolve(this.context.cwd, arg)); } else { return this.installFromDescriptor(parseSpec(arg, `CLI arguments`, { enforceExactVersion: false })); } @@ -23295,7 +23288,7 @@ var InstallGlobalCommand = class extends BaseCommand { if (!isSupportedPackageManager(name2)) throw new UsageError(`Unsupported package manager '${name2}'`); this.log({ name: name2, reference }); - await import_fs13.default.promises.mkdir(installFolder, { recursive: true }); + await import_fs10.default.promises.mkdir(installFolder, { recursive: true }); await tarX({ file: p, cwd: installFolder }, [`${name2}/${reference}`]); if (!this.cacheOnly) { await this.context.engine.activatePackageManager({ name: name2, reference }); @@ -23334,8 +23327,8 @@ var InstallLocalCommand = class extends BaseCommand { }; // sources/commands/Pack.ts -var import_promises2 = require("fs/promises"); -var import_path15 = __toESM(require("path")); +var import_promises3 = require("fs/promises"); +var import_path11 = __toESM(require("path")); var PackCommand = class extends BaseCommand { static paths = [ [`pack`] @@ -23376,17 +23369,17 @@ var PackCommand = class extends BaseCommand { installLocations.push(packageManagerInfo.location); } const baseInstallFolder = getInstallFolder(); - const outputPath = import_path15.default.resolve(this.context.cwd, this.output ?? `corepack.tgz`); + const outputPath = import_path11.default.resolve(this.context.cwd, this.output ?? `corepack.tgz`); if (!this.json) { this.context.stdout.write(` `); - this.context.stdout.write(`Packing the selected tools in ${import_path15.default.basename(outputPath)}... + this.context.stdout.write(`Packing the selected tools in ${import_path11.default.basename(outputPath)}... `); } const { create: tarC } = await Promise.resolve().then(() => (init_create(), create_exports)); - await (0, import_promises2.mkdir)(baseInstallFolder, { recursive: true }); - await tarC({ gzip: true, cwd: baseInstallFolder, file: import_path15.default.resolve(outputPath) }, installLocations.map((location) => { - return import_path15.default.relative(baseInstallFolder, location); + await (0, import_promises3.mkdir)(baseInstallFolder, { recursive: true }); + await tarC({ gzip: true, cwd: baseInstallFolder, file: import_path11.default.resolve(outputPath) }, installLocations.map((location) => { + return import_path11.default.relative(baseInstallFolder, location); })); if (this.json) { this.context.stdout.write(`${JSON.stringify(outputPath)} @@ -23477,8 +23470,8 @@ var UseCommand = class extends BaseCommand { }; // sources/commands/deprecated/Hydrate.ts -var import_promises3 = require("fs/promises"); -var import_path16 = __toESM(require("path")); +var import_promises4 = require("fs/promises"); +var import_path12 = __toESM(require("path")); var HydrateCommand = class extends Command { static paths = [ [`hydrate`] @@ -23489,7 +23482,7 @@ var HydrateCommand = class extends Command { fileName = options_exports.String(); async execute() { const installFolder = getInstallFolder(); - const fileName = import_path16.default.resolve(this.context.cwd, this.fileName); + const fileName = import_path12.default.resolve(this.context.cwd, this.fileName); const archiveEntries = /* @__PURE__ */ new Map(); let hasShortEntries = false; const { list: tarT } = await Promise.resolve().then(() => (init_list(), list_exports)); @@ -23517,7 +23510,7 @@ var HydrateCommand = class extends Command { else this.context.stdout.write(`Hydrating ${name2}@${reference}... `); - await (0, import_promises3.mkdir)(installFolder, { recursive: true }); + await (0, import_promises4.mkdir)(installFolder, { recursive: true }); await tarX({ file: fileName, cwd: installFolder }, [`${name2}/${reference}`]); if (this.activate) { await this.context.engine.activatePackageManager({ name: name2, reference }); @@ -23530,8 +23523,8 @@ var HydrateCommand = class extends Command { }; // sources/commands/deprecated/Prepare.ts -var import_promises4 = require("fs/promises"); -var import_path17 = __toESM(require("path")); +var import_promises5 = require("fs/promises"); +var import_path13 = __toESM(require("path")); var PrepareCommand = class extends Command { static paths = [ [`prepare`] @@ -23585,14 +23578,14 @@ var PrepareCommand = class extends Command { if (this.output) { const outputName = typeof this.output === `string` ? this.output : `corepack.tgz`; const baseInstallFolder = getInstallFolder(); - const outputPath = import_path17.default.resolve(this.context.cwd, outputName); + const outputPath = import_path13.default.resolve(this.context.cwd, outputName); if (!this.json) - this.context.stdout.write(`Packing the selected tools in ${import_path17.default.basename(outputPath)}... + this.context.stdout.write(`Packing the selected tools in ${import_path13.default.basename(outputPath)}... `); const { create: tarC } = await Promise.resolve().then(() => (init_create(), create_exports)); - await (0, import_promises4.mkdir)(baseInstallFolder, { recursive: true }); - await tarC({ gzip: true, cwd: baseInstallFolder, file: import_path17.default.resolve(outputPath) }, installLocations.map((location) => { - return import_path17.default.relative(baseInstallFolder, location); + await (0, import_promises5.mkdir)(baseInstallFolder, { recursive: true }); + await tarC({ gzip: true, cwd: baseInstallFolder, file: import_path13.default.resolve(outputPath) }, installLocations.map((location) => { + return import_path13.default.relative(baseInstallFolder, location); })); if (this.json) { this.context.stdout.write(`${JSON.stringify(outputPath)} diff --git a/deps/corepack/package.json b/deps/corepack/package.json index baa2a678c8b092..245fc175913e42 100644 --- a/deps/corepack/package.json +++ b/deps/corepack/package.json @@ -1,6 +1,6 @@ { "name": "corepack", - "version": "0.34.0", + "version": "0.34.1", "homepage": "https://github.com/nodejs/corepack#readme", "bugs": { "url": "https://github.com/nodejs/corepack/issues" @@ -16,7 +16,7 @@ "./package.json": "./package.json" }, "license": "MIT", - "packageManager": "yarn@4.9.0+sha224.dce6c5df199861784bd9b0eecb2a228df97e3f18a02b1bb75ff98383", + "packageManager": "yarn@4.10.3+sha224.6020b3cdcdfbd7dbc24b7a7b75d58a249ce36068a8bf97d39aa8cc6d", "devDependencies": { "@types/debug": "^4.1.5", "@types/node": "^20.4.6", @@ -37,7 +37,7 @@ "tar": "^7.4.0", "tsx": "^4.16.2", "typescript": "^5.7.3", - "undici": "^6.19.2", + "undici": "^6.21.2", "v8-compile-cache": "^2.3.0", "vitest": "^3.0.5", "which": "^5.0.0" diff --git a/deps/googletest/include/gtest/gtest-matchers.h b/deps/googletest/include/gtest/gtest-matchers.h index 93643dba1d3a07..bd8dfe999d3681 100644 --- a/deps/googletest/include/gtest/gtest-matchers.h +++ b/deps/googletest/include/gtest/gtest-matchers.h @@ -296,12 +296,12 @@ class MatcherBase : private MatcherDescriberInterface { return *this; } - MatcherBase(MatcherBase&& other) + MatcherBase(MatcherBase&& other) noexcept : vtable_(other.vtable_), buffer_(other.buffer_) { other.vtable_ = nullptr; } - MatcherBase& operator=(MatcherBase&& other) { + MatcherBase& operator=(MatcherBase&& other) noexcept { if (this == &other) return *this; Destroy(); vtable_ = other.vtable_; diff --git a/deps/inspector_protocol/README.node b/deps/inspector_protocol/README.node index af4618cd6160e5..f37ddb9e9280b6 100644 --- a/deps/inspector_protocol/README.node +++ b/deps/inspector_protocol/README.node @@ -2,7 +2,7 @@ Name: inspector protocol Short Name: inspector_protocol URL: https://chromium.googlesource.com/deps/inspector_protocol/ Version: 0 -Revision: 69d69ddf3aa698b171886551a4a672c5af1ad902 +Revision: af7f5a8173fdbc29f0835ec94395932e328b2ea2 License: BSD License File: LICENSE Security Critical: no diff --git a/deps/inspector_protocol/crdtp/cbor.cc b/deps/inspector_protocol/crdtp/cbor.cc index 801c1708022763..dc52549c55a6fe 100644 --- a/deps/inspector_protocol/crdtp/cbor.cc +++ b/deps/inspector_protocol/crdtp/cbor.cc @@ -833,7 +833,7 @@ void ParseUTF16String(CBORTokenizer* tokenizer, ParserHandler* out) { span rep = tokenizer->GetString16WireRep(); for (size_t ii = 0; ii < rep.size(); ii += 2) value.push_back((rep[ii + 1] << 8) | rep[ii]); - out->HandleString16(span(value.data(), value.size())); + out->HandleString16(value); tokenizer->Next(); } @@ -1037,7 +1037,7 @@ void ParseCBOR(span bytes, ParserHandler* out) { Status AppendString8EntryToCBORMap(span string8_key, span string8_value, std::vector* cbor) { - span bytes(cbor->data(), cbor->size()); + span bytes(*cbor); CBORTokenizer tokenizer(bytes); if (tokenizer.TokenTag() == CBORTokenTag::ERROR_VALUE) return tokenizer.Status(); diff --git a/deps/inspector_protocol/crdtp/cbor_test.cc b/deps/inspector_protocol/crdtp/cbor_test.cc index 058cd787d6416b..2d3689e730047c 100644 --- a/deps/inspector_protocol/crdtp/cbor_test.cc +++ b/deps/inspector_protocol/crdtp/cbor_test.cc @@ -349,7 +349,7 @@ TEST(EncodeDecodeString16Test, RoundtripsHelloWorld) { std::array msg{ {'H', 'e', 'l', 'l', 'o', ',', ' ', 0xd83c, 0xdf0e, '.'}}; std::vector encoded; - EncodeString16(span(msg.data(), msg.size()), &encoded); + EncodeString16(msg, &encoded); // This will be encoded as BYTE_STRING of length 20, so the 20 is encoded in // the additional info part of the initial byte. Payload is two bytes for each // UTF16 character. @@ -384,7 +384,7 @@ TEST(EncodeDecodeString16Test, Roundtrips500) { for (uint16_t ii = 0; ii < 250; ++ii) two_fifty.push_back(ii); std::vector encoded; - EncodeString16(span(two_fifty.data(), two_fifty.size()), &encoded); + EncodeString16(two_fifty, &encoded); EXPECT_EQ(3u + 250u * 2, encoded.size()); // Now check the first three bytes: // Major type: 2 (BYTE_STRING) @@ -501,7 +501,7 @@ TEST(EncodeFromLatin1Test, ConvertsToUTF8IfNeeded) { TEST(EncodeFromUTF16Test, ConvertsToUTF8IfEasy) { std::vector ascii = {'e', 'a', 's', 'y'}; std::vector encoded; - EncodeFromUTF16(span(ascii.data(), ascii.size()), &encoded); + EncodeFromUTF16(ascii, &encoded); CBORTokenizer tokenizer(SpanFrom(encoded)); EXPECT_EQ(CBORTokenTag::STRING8, tokenizer.TokenTag()); @@ -518,7 +518,7 @@ TEST(EncodeFromUTF16Test, EncodesAsString16IfNeeded) { std::vector msg = {'H', 'e', 'l', 'l', 'o', ',', ' ', 0xd83c, 0xdf0e, '.'}; std::vector encoded; - EncodeFromUTF16(span(msg.data(), msg.size()), &encoded); + EncodeFromUTF16(msg, &encoded); CBORTokenizer tokenizer(SpanFrom(encoded)); EXPECT_EQ(CBORTokenTag::STRING16, tokenizer.TokenTag()); @@ -535,7 +535,7 @@ TEST(EncodeDecodeBinaryTest, RoundtripsHelloWorld) { std::vector binary = {'H', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '.'}; std::vector encoded; - EncodeBinary(span(binary.data(), binary.size()), &encoded); + EncodeBinary(binary, &encoded); // So, on the wire we see that the binary blob travels unmodified. EXPECT_THAT( encoded, @@ -699,7 +699,7 @@ TEST(JSONToCBOREncoderTest, SevenBitStrings) { Status status; std::unique_ptr encoder = NewCBOREncoder(&encoded, &status); std::vector utf16 = {'f', 'o', 'o'}; - encoder->HandleString16(span(utf16.data(), utf16.size())); + encoder->HandleString16(utf16); EXPECT_THAT(status, StatusIsOk()); // Here we assert that indeed, seven bit strings are represented as // bytes on the wire, "foo" is just "foo". @@ -771,7 +771,7 @@ TEST(JsonCborRoundtrip, EncodingDecoding) { std::string decoded; std::unique_ptr json_encoder = json::NewJSONEncoder(&decoded, &status); - ParseCBOR(span(encoded.data(), encoded.size()), json_encoder.get()); + ParseCBOR(encoded, json_encoder.get()); EXPECT_THAT(status, StatusIsOk()); EXPECT_EQ(json, decoded); } @@ -791,7 +791,7 @@ TEST(JsonCborRoundtrip, MoreRoundtripExamples) { std::string decoded; std::unique_ptr json_writer = json::NewJSONEncoder(&decoded, &status); - ParseCBOR(span(encoded.data(), encoded.size()), json_writer.get()); + ParseCBOR(encoded, json_writer.get()); EXPECT_THAT(status, StatusIsOk()); EXPECT_EQ(json, decoded); } @@ -842,7 +842,7 @@ TEST(ParseCBORTest, ParseEmptyCBORMessage) { Status status; std::unique_ptr json_writer = json::NewJSONEncoder(&out, &status); - ParseCBOR(span(in.data(), in.size()), json_writer.get()); + ParseCBOR(in, json_writer.get()); EXPECT_THAT(status, StatusIsOk()); EXPECT_EQ("{}", out); } @@ -866,7 +866,7 @@ TEST(ParseCBORTest, ParseCBORHelloWorld) { Status status; std::unique_ptr json_writer = json::NewJSONEncoder(&out, &status); - ParseCBOR(span(bytes.data(), bytes.size()), json_writer.get()); + ParseCBOR(bytes, json_writer.get()); EXPECT_THAT(status, StatusIsOk()); EXPECT_EQ("{\"msg\":\"Hello, \\ud83c\\udf0e.\"}", out); } @@ -887,7 +887,7 @@ TEST(ParseCBORTest, UTF8IsSupportedInKeys) { Status status; std::unique_ptr json_writer = json::NewJSONEncoder(&out, &status); - ParseCBOR(span(bytes.data(), bytes.size()), json_writer.get()); + ParseCBOR(bytes, json_writer.get()); EXPECT_THAT(status, StatusIsOk()); EXPECT_EQ("{\"\\ud83c\\udf0e\":\"\\u263e\"}", out); } @@ -898,7 +898,7 @@ TEST(ParseCBORTest, NoInputError) { Status status; std::unique_ptr json_writer = json::NewJSONEncoder(&out, &status); - ParseCBOR(span(in.data(), in.size()), json_writer.get()); + ParseCBOR(in, json_writer.get()); EXPECT_THAT(status, StatusIs(Error::CBOR_UNEXPECTED_EOF_IN_ENVELOPE, 0u)); EXPECT_EQ("", out); } @@ -914,7 +914,7 @@ TEST(ParseCBORTest, UnexpectedEofExpectedValueError) { Status status; std::unique_ptr json_writer = json::NewJSONEncoder(&out, &status); - ParseCBOR(span(bytes.data(), bytes.size()), json_writer.get()); + ParseCBOR(bytes, json_writer.get()); EXPECT_THAT(status, StatusIs(Error::CBOR_UNEXPECTED_EOF_EXPECTED_VALUE, bytes.size())); EXPECT_EQ("", out); @@ -932,7 +932,7 @@ TEST(ParseCBORTest, UnexpectedEofInArrayError) { Status status; std::unique_ptr json_writer = json::NewJSONEncoder(&out, &status); - ParseCBOR(span(bytes.data(), bytes.size()), json_writer.get()); + ParseCBOR(bytes, json_writer.get()); EXPECT_THAT(status, StatusIs(Error::CBOR_UNEXPECTED_EOF_IN_ARRAY, bytes.size())); EXPECT_EQ("", out); @@ -947,7 +947,7 @@ TEST(ParseCBORTest, UnexpectedEofInMapError) { Status status; std::unique_ptr json_writer = json::NewJSONEncoder(&out, &status); - ParseCBOR(span(bytes.data(), bytes.size()), json_writer.get()); + ParseCBOR(bytes, json_writer.get()); EXPECT_THAT(status, StatusIs(Error::CBOR_UNEXPECTED_EOF_IN_MAP, 7u)); EXPECT_EQ("", out); } @@ -963,7 +963,7 @@ TEST(ParseCBORTest, EnvelopeEncodingLegacy) { Status status; std::unique_ptr json_writer = json::NewJSONEncoder(&out, &status); - ParseCBOR(span(bytes.data(), bytes.size()), json_writer.get()); + ParseCBOR(bytes, json_writer.get()); EXPECT_THAT(status, StatusIsOk()); EXPECT_EQ(out, "{\"foo\":42}"); } @@ -980,7 +980,7 @@ TEST(ParseCBORTest, EnvelopeEncodingBySpec) { Status status; std::unique_ptr json_writer = json::NewJSONEncoder(&out, &status); - ParseCBOR(span(bytes.data(), bytes.size()), json_writer.get()); + ParseCBOR(bytes, json_writer.get()); EXPECT_THAT(status, StatusIsOk()); EXPECT_EQ(out, "{\"foo\":42}"); } @@ -991,7 +991,7 @@ TEST(ParseCBORTest, NoEmptyEnvelopesAllowed) { Status status; std::unique_ptr json_writer = json::NewJSONEncoder(&out, &status); - ParseCBOR(span(bytes.data(), bytes.size()), json_writer.get()); + ParseCBOR(bytes, json_writer.get()); EXPECT_THAT(status, StatusIs(Error::CBOR_MAP_OR_ARRAY_EXPECTED_IN_ENVELOPE, bytes.size())); EXPECT_EQ("", out); @@ -1021,7 +1021,7 @@ TEST(ParseCBORTest, OnlyMapsAndArraysSupportedInsideEnvelopes) { Status status; std::unique_ptr json_writer = json::NewJSONEncoder(&out, &status); - ParseCBOR(span(bytes.data(), bytes.size()), json_writer.get()); + ParseCBOR(bytes, json_writer.get()); EXPECT_THAT(status, StatusIs(Error::CBOR_MAP_OR_ARRAY_EXPECTED_IN_ENVELOPE, error_pos)); EXPECT_EQ("", out); @@ -1038,7 +1038,7 @@ TEST(ParseCBORTest, InvalidMapKeyError) { Status status; std::unique_ptr json_writer = json::NewJSONEncoder(&out, &status); - ParseCBOR(span(bytes.data(), bytes.size()), json_writer.get()); + ParseCBOR(bytes, json_writer.get()); EXPECT_THAT(status, StatusIs(Error::CBOR_INVALID_MAP_KEY, 7u)); EXPECT_EQ("", out); } @@ -1068,7 +1068,7 @@ TEST(ParseCBORTest, StackLimitExceededError) { Status status; std::unique_ptr json_writer = json::NewJSONEncoder(&out, &status); - ParseCBOR(span(bytes.data(), bytes.size()), json_writer.get()); + ParseCBOR(bytes, json_writer.get()); EXPECT_THAT(status, StatusIsOk()); EXPECT_EQ("{\"key\":{\"key\":{\"key\":\"innermost_value\"}}}", out); } @@ -1078,7 +1078,7 @@ TEST(ParseCBORTest, StackLimitExceededError) { Status status; std::unique_ptr json_writer = json::NewJSONEncoder(&out, &status); - ParseCBOR(span(bytes.data(), bytes.size()), json_writer.get()); + ParseCBOR(bytes, json_writer.get()); EXPECT_THAT(status, StatusIsOk()); } @@ -1097,7 +1097,7 @@ TEST(ParseCBORTest, StackLimitExceededError) { Status status; std::unique_ptr json_writer = json::NewJSONEncoder(&out, &status); - ParseCBOR(span(bytes.data(), bytes.size()), json_writer.get()); + ParseCBOR(bytes, json_writer.get()); EXPECT_THAT(status, StatusIs(Error::CBOR_STACK_LIMIT_EXCEEDED, opening_segment_size * 301)); } @@ -1107,7 +1107,7 @@ TEST(ParseCBORTest, StackLimitExceededError) { Status status; std::unique_ptr json_writer = json::NewJSONEncoder(&out, &status); - ParseCBOR(span(bytes.data(), bytes.size()), json_writer.get()); + ParseCBOR(bytes, json_writer.get()); EXPECT_THAT(status, StatusIs(Error::CBOR_STACK_LIMIT_EXCEEDED, opening_segment_size * 301)); } @@ -1126,7 +1126,7 @@ TEST(ParseCBORTest, UnsupportedValueError) { Status status; std::unique_ptr json_writer = json::NewJSONEncoder(&out, &status); - ParseCBOR(span(bytes.data(), bytes.size()), json_writer.get()); + ParseCBOR(bytes, json_writer.get()); EXPECT_THAT(status, StatusIs(Error::CBOR_UNSUPPORTED_VALUE, error_pos)); EXPECT_EQ("", out); } @@ -1148,7 +1148,7 @@ TEST(ParseCBORTest, InvalidString16Error) { Status status; std::unique_ptr json_writer = json::NewJSONEncoder(&out, &status); - ParseCBOR(span(bytes.data(), bytes.size()), json_writer.get()); + ParseCBOR(bytes, json_writer.get()); EXPECT_THAT(status, StatusIs(Error::CBOR_INVALID_STRING16, error_pos)); EXPECT_EQ("", out); } @@ -1167,7 +1167,7 @@ TEST(ParseCBORTest, InvalidString8Error) { Status status; std::unique_ptr json_writer = json::NewJSONEncoder(&out, &status); - ParseCBOR(span(bytes.data(), bytes.size()), json_writer.get()); + ParseCBOR(bytes, json_writer.get()); EXPECT_THAT(status, StatusIs(Error::CBOR_INVALID_STRING8, error_pos)); EXPECT_EQ("", out); } @@ -1188,7 +1188,7 @@ TEST(ParseCBORTest, InvalidBinaryError) { Status status; std::unique_ptr json_writer = json::NewJSONEncoder(&out, &status); - ParseCBOR(span(bytes.data(), bytes.size()), json_writer.get()); + ParseCBOR(bytes, json_writer.get()); EXPECT_THAT(status, StatusIs(Error::CBOR_INVALID_BINARY, error_pos)); EXPECT_EQ("", out); } @@ -1208,7 +1208,7 @@ TEST(ParseCBORTest, InvalidDoubleError) { Status status; std::unique_ptr json_writer = json::NewJSONEncoder(&out, &status); - ParseCBOR(span(bytes.data(), bytes.size()), json_writer.get()); + ParseCBOR(bytes, json_writer.get()); EXPECT_THAT(status, StatusIs(Error::CBOR_INVALID_DOUBLE, error_pos)); EXPECT_EQ("", out); } @@ -1228,7 +1228,7 @@ TEST(ParseCBORTest, InvalidSignedError) { Status status; std::unique_ptr json_writer = json::NewJSONEncoder(&out, &status); - ParseCBOR(span(bytes.data(), bytes.size()), json_writer.get()); + ParseCBOR(bytes, json_writer.get()); EXPECT_THAT(status, StatusIs(Error::CBOR_INVALID_INT32, error_pos)); EXPECT_EQ("", out); } @@ -1250,7 +1250,7 @@ TEST(ParseCBORTest, TrailingJunk) { Status status; std::unique_ptr json_writer = json::NewJSONEncoder(&out, &status); - ParseCBOR(span(bytes.data(), bytes.size()), json_writer.get()); + ParseCBOR(bytes, json_writer.get()); EXPECT_THAT(status, StatusIs(Error::CBOR_TRAILING_JUNK, error_pos)); EXPECT_EQ("", out); } @@ -1272,7 +1272,7 @@ TEST(ParseCBORTest, EnvelopeContentsLengthMismatch) { Status status; std::unique_ptr json_writer = json::NewJSONEncoder(&out, &status); - ParseCBOR(span(bytes.data(), bytes.size()), json_writer.get()); + ParseCBOR(bytes, json_writer.get()); EXPECT_THAT(status, StatusIs(Error::CBOR_ENVELOPE_CONTENTS_LENGTH_MISMATCH, bytes.size())); EXPECT_EQ("", out); diff --git a/deps/inspector_protocol/crdtp/json.cc b/deps/inspector_protocol/crdtp/json.cc index 479f9ba51875ab..bc3b8a6d9093c7 100644 --- a/deps/inspector_protocol/crdtp/json.cc +++ b/deps/inspector_protocol/crdtp/json.cc @@ -149,23 +149,31 @@ class JSONEncoder : public ParserHandler { Emit('"'); for (const uint16_t ch : chars) { if (ch == '"') { - Emit('\\'); Emit('"'); + Emit('\\'); + Emit('"'); } else if (ch == '\\') { - Emit('\\'); Emit('\\'); + Emit('\\'); + Emit('\\'); } else if (ch >= 32 && ch <= 127) { Emit(ch); } else if (ch == '\n') { - Emit('\\'); Emit('n'); + Emit('\\'); + Emit('n'); } else if (ch == '\r') { - Emit('\\'); Emit('r'); + Emit('\\'); + Emit('r'); } else if (ch == '\t') { - Emit('\\'); Emit('t'); + Emit('\\'); + Emit('t'); } else if (ch == '\b') { - Emit('\\'); Emit('b'); + Emit('\\'); + Emit('b'); } else if (ch == '\f') { - Emit('\\'); Emit('f'); + Emit('\\'); + Emit('f'); } else { - Emit('\\'); Emit('u'); + Emit('\\'); + Emit('u'); PrintHex(ch, out_); } } @@ -177,26 +185,42 @@ class JSONEncoder : public ParserHandler { return; state_.top().StartElement(out_); Emit('"'); + // Fast path for input strings that can be emitted as-is. + if (std::all_of(chars.begin(), chars.end(), [](uint8_t c) { + return c != '"' && c != '\\' && c >= 32 && c <= 127; + })) { + Emit(chars); + Emit('"'); + return; + } for (size_t ii = 0; ii < chars.size(); ++ii) { uint8_t c = chars[ii]; if (c == '"') { - Emit('\\'); Emit('"'); + Emit('\\'); + Emit('"'); } else if (c == '\\') { - Emit('\\'); Emit('\\'); + Emit('\\'); + Emit('\\'); } else if (c >= 32 && c <= 127) { Emit(c); } else if (c == '\n') { - Emit('\\'); Emit('n'); + Emit('\\'); + Emit('n'); } else if (c == '\r') { - Emit('\\'); Emit('r'); + Emit('\\'); + Emit('r'); } else if (c == '\t') { - Emit('\\'); Emit('t'); + Emit('\\'); + Emit('t'); } else if (c == '\b') { - Emit('\\'); Emit('b'); + Emit('\\'); + Emit('b'); } else if (c == '\f') { - Emit('\\'); Emit('f'); + Emit('\\'); + Emit('f'); } else if (c < 32) { - Emit('\\'); Emit('u'); + Emit('\\'); + Emit('u'); PrintHex(static_cast(c), out_); } else { // Inspect the leading byte to figure out how long the utf8 @@ -330,10 +354,11 @@ class JSONEncoder : public ParserHandler { if (!status_->ok()) return; state_.top().StartElement(out_); - if (value) + if (value) { Emit("true"); - else + } else { Emit("false"); + } } void HandleNull() override { @@ -351,13 +376,16 @@ class JSONEncoder : public ParserHandler { private: inline void Emit(char c) { out_->push_back(c); } - template + template inline void Emit(const char (&str)[N]) { out_->insert(out_->end(), str, str + N - 1); } inline void Emit(const std::string& str) { out_->insert(out_->end(), str.begin(), str.end()); } + inline void Emit(const span& bytes) { + out_->insert(out_->end(), bytes.begin(), bytes.end()); + } C* out_; Status* status_; @@ -882,7 +910,7 @@ class JsonParser { HandleError(Error::JSON_PARSER_INVALID_STRING, token_start); return; } - handler_->HandleString16(span(value.data(), value.size())); + handler_->HandleString16(value); break; } case ArrayBegin: { @@ -929,7 +957,7 @@ class JsonParser { HandleError(Error::JSON_PARSER_INVALID_STRING, token_start); return; } - handler_->HandleString16(span(key.data(), key.size())); + handler_->HandleString16(key); start = token_end; token = ParseToken(start, end, &token_start, &token_end); diff --git a/deps/inspector_protocol/crdtp/json_test.cc b/deps/inspector_protocol/crdtp/json_test.cc index 08f65e48a20147..13566ac8b92f61 100644 --- a/deps/inspector_protocol/crdtp/json_test.cc +++ b/deps/inspector_protocol/crdtp/json_test.cc @@ -90,7 +90,7 @@ TEST(JsonEncoder, EscapesLoneHighSurrogates) { std::string out; Status status; std::unique_ptr writer = NewJSONEncoder(&out, &status); - writer->HandleString16(span(chars.data(), chars.size())); + writer->HandleString16(chars); EXPECT_EQ("\"a\\ud800b\\udadac\\udbffd\"", out); } @@ -103,7 +103,7 @@ TEST(JsonEncoder, EscapesLoneLowSurrogates) { std::string out; Status status; std::unique_ptr writer = NewJSONEncoder(&out, &status); - writer->HandleString16(span(chars.data(), chars.size())); + writer->HandleString16(chars); EXPECT_EQ("\"a\\udc00b\\udedec\\udfffd\"", out); } @@ -114,7 +114,7 @@ TEST(JsonEncoder, EscapesFFFF) { std::string out; Status status; std::unique_ptr writer = NewJSONEncoder(&out, &status); - writer->HandleString16(span(chars.data(), chars.size())); + writer->HandleString16(chars); EXPECT_EQ("\"abc\\uffffd\"", out); } @@ -123,7 +123,7 @@ TEST(JsonEncoder, Passes0x7FString8) { std::string out; Status status; std::unique_ptr writer = NewJSONEncoder(&out, &status); - writer->HandleString8(span(chars.data(), chars.size())); + writer->HandleString8(chars); EXPECT_EQ( "\"a\x7f" "b\"", @@ -135,7 +135,7 @@ TEST(JsonEncoder, Passes0x7FString16) { std::string out; Status status; std::unique_ptr writer = NewJSONEncoder(&out, &status); - writer->HandleString16(span(chars16.data(), chars16.size())); + writer->HandleString16(chars16); EXPECT_EQ( "\"a\x7f" "b\"", @@ -737,8 +737,7 @@ TYPED_TEST(ConvertJSONToCBORTest, RoundTripValidJson16) { '"', ':', '[', '1', ',', '2', ',', '3', ']', '}'}; std::vector cbor; { - Status status = - ConvertJSONToCBOR(span(json16.data(), json16.size()), &cbor); + Status status = ConvertJSONToCBOR(json16, &cbor); EXPECT_THAT(status, StatusIsOk()); } TypeParam roundtrip_json; diff --git a/deps/inspector_protocol/crdtp/protocol_core.cc b/deps/inspector_protocol/crdtp/protocol_core.cc index 69773593ded162..dc483ac5ad0896 100644 --- a/deps/inspector_protocol/crdtp/protocol_core.cc +++ b/deps/inspector_protocol/crdtp/protocol_core.cc @@ -12,7 +12,7 @@ namespace crdtp { DeserializerState::DeserializerState(std::vector bytes) : storage_(new std::vector(std::move(bytes))), - tokenizer_(span(storage_->data(), storage_->size())) {} + tokenizer_(*storage_) {} DeserializerState::DeserializerState(Storage storage, span span) : storage_(std::move(storage)), tokenizer_(span) {} diff --git a/deps/inspector_protocol/crdtp/protocol_core.h b/deps/inspector_protocol/crdtp/protocol_core.h index f8283367773a42..b111746253aaf3 100644 --- a/deps/inspector_protocol/crdtp/protocol_core.h +++ b/deps/inspector_protocol/crdtp/protocol_core.h @@ -219,8 +219,7 @@ struct CRDTP_EXPORT ProtocolTypeTraits { template struct ProtocolTypeTraits> { - static bool Deserialize(DeserializerState* state, - std::optional* value) { + static bool Deserialize(DeserializerState* state, std::optional* value) { T res; if (!ProtocolTypeTraits::Deserialize(state, &res)) return false; @@ -341,9 +340,7 @@ template bool ConvertProtocolValue(const F& from, T* to) { std::vector bytes; ProtocolTypeTraits::Serialize(from, &bytes); - auto deserializer = - DeferredMessage::FromSpan(span(bytes.data(), bytes.size())) - ->MakeDeserializer(); + auto deserializer = DeferredMessage::FromSpan(bytes)->MakeDeserializer(); return ProtocolTypeTraits::Deserialize(&deserializer, to); } diff --git a/deps/inspector_protocol/crdtp/span.h b/deps/inspector_protocol/crdtp/span.h index c2b5b02d9a1832..40471f20aa88a2 100644 --- a/deps/inspector_protocol/crdtp/span.h +++ b/deps/inspector_protocol/crdtp/span.h @@ -7,49 +7,17 @@ #include #include -#include +#include #include +#include #include "export.h" namespace crdtp { -// ============================================================================= -// span - sequence of bytes -// ============================================================================= -// This template is similar to std::span, which will be included in C++20. +// crdtp::span is a std::span which always holds const elements. template -class span { - public: - using index_type = size_t; - - constexpr span() : data_(nullptr), size_(0) {} - constexpr span(const T* data, index_type size) : data_(data), size_(size) {} - - constexpr const T* data() const { return data_; } - - constexpr const T* begin() const { return data_; } - constexpr const T* end() const { return data_ + size_; } - - constexpr const T& operator[](index_type idx) const { return data_[idx]; } - - constexpr span subspan(index_type offset, index_type count) const { - return span(data_ + offset, count); - } - - constexpr span subspan(index_type offset) const { - return span(data_ + offset, size_ - offset); - } - - constexpr bool empty() const { return size_ == 0; } - - constexpr index_type size() const { return size_; } - constexpr index_type size_bytes() const { return size_ * sizeof(T); } - - private: - const T* data_; - index_type size_; -}; +using span = std::span; template constexpr span MakeSpan(const char (&str)[N]) { diff --git a/deps/inspector_protocol/crdtp/span_test.cc b/deps/inspector_protocol/crdtp/span_test.cc index e562f4ea6feed9..2845a3b889fcc3 100644 --- a/deps/inspector_protocol/crdtp/span_test.cc +++ b/deps/inspector_protocol/crdtp/span_test.cc @@ -85,6 +85,12 @@ TEST(SpanFromTest, FromVectorUint8AndUint16) { EXPECT_EQ(bar.size(), bar_span.size()); } +TEST(SpanFromTest, FromVectorImplicit) { + std::vector foo = {'f', 'o', 'o'}; + span foo_span(foo); + EXPECT_EQ(foo.size(), foo_span.size()); +} + TEST(SpanComparisons, ByteWiseLexicographicalOrder) { // Compare the empty span. EXPECT_FALSE(SpanLessThan(span(), span())); diff --git a/deps/inspector_protocol/lib/Values_cpp.template b/deps/inspector_protocol/lib/Values_cpp.template index 0c409295d52ae3..abeee79e5c3a05 100644 --- a/deps/inspector_protocol/lib/Values_cpp.template +++ b/deps/inspector_protocol/lib/Values_cpp.template @@ -300,7 +300,7 @@ namespace { // transcodes to UTF8 if needed. void EncodeString(const String& s, std::vector* out) { if (StringUtil::CharacterCount(s) == 0) { - cbor::EncodeString8(span(nullptr, 0), out); // Empty string. + cbor::EncodeString8(span(), out); // Empty string. } else if (StringUtil::CharactersLatin1(s)) { cbor::EncodeFromLatin1(span(StringUtil::CharactersLatin1(s), StringUtil::CharacterCount(s)), diff --git a/deps/inspector_protocol/pdl.py b/deps/inspector_protocol/pdl.py index 6b448c07443c7e..20f505ea81e34e 100644 --- a/deps/inspector_protocol/pdl.py +++ b/deps/inspector_protocol/pdl.py @@ -86,6 +86,18 @@ def parse(data, file_name, map_binary_to_string=False): protocol['domains'].append(domain) continue + match = re.compile( + r'^include (.*)').match(line) + if match: + included_filename = match.group(1) + if path.isabs(included_filename): + raise Exception("Only relative paths are supported in include's") + resolved_path = path.normpath(path.join(path.dirname(file_name), included_filename)) + with open(resolved_path, 'r') as file: + included_data = parse(file.read(), resolved_path, map_binary_to_string) + protocol['domains'].extend(included_data['domains']) + continue + match = re.compile(r'^ depends on ([^\s]+)').match(line) if match: if 'dependencies' not in domain: diff --git a/deps/simdjson/simdjson.cpp b/deps/simdjson/simdjson.cpp index 819942e8289b69..8a73ef4004c360 100644 --- a/deps/simdjson/simdjson.cpp +++ b/deps/simdjson/simdjson.cpp @@ -1,4 +1,4 @@ -/* auto-generated on 2025-06-04 00:22:10 -0400. Do not edit! */ +/* auto-generated on 2025-09-29 20:34:35 -0700. version 4.0.7 Do not edit! */ /* including simdjson.cpp: */ /* begin file simdjson.cpp */ #define SIMDJSON_SRC_SIMDJSON_CPP @@ -77,30 +77,71 @@ #endif #endif +#ifndef SIMDJSON_CONSTEXPR_LAMBDA +#if SIMDJSON_CPLUSPLUS17 +#define SIMDJSON_CONSTEXPR_LAMBDA constexpr +#else +#define SIMDJSON_CONSTEXPR_LAMBDA +#endif +#endif + + + #ifdef __has_include #if __has_include() #include #endif #endif +// The current specification is unclear on how we detect +// static reflection, both __cpp_lib_reflection and +// __cpp_impl_reflection are proposed in the draft specification. +// For now, we disable static reflect by default. It must be +// specified at compiler time. +#ifndef SIMDJSON_STATIC_REFLECTION +#define SIMDJSON_STATIC_REFLECTION 0 // disabled by default. +#endif + #if defined(__apple_build_version__) #if __apple_build_version__ < 14000000 #define SIMDJSON_CONCEPT_DISABLED 1 // apple-clang/13 doesn't support std::convertible_to #endif #endif +#if defined(__cpp_lib_ranges) && __cpp_lib_ranges >= 201911L +#include +#define SIMDJSON_SUPPORTS_RANGES 1 +#else +#define SIMDJSON_SUPPORTS_RANGES 0 +#endif #if defined(__cpp_concepts) && !defined(SIMDJSON_CONCEPT_DISABLED) #if __cpp_concepts >= 201907L #include -#define SIMDJSON_SUPPORTS_DESERIALIZATION 1 +#define SIMDJSON_SUPPORTS_CONCEPTS 1 #else -#define SIMDJSON_SUPPORTS_DESERIALIZATION 0 +#define SIMDJSON_SUPPORTS_CONCEPTS 0 #endif #else // defined(__cpp_concepts) && !defined(SIMDJSON_CONCEPT_DISABLED) -#define SIMDJSON_SUPPORTS_DESERIALIZATION 0 +#define SIMDJSON_SUPPORTS_CONCEPTS 0 #endif // defined(__cpp_concepts) && !defined(SIMDJSON_CONCEPT_DISABLED) +// copy SIMDJSON_SUPPORTS_CONCEPTS to SIMDJSON_SUPPORTS_DESERIALIZATION. +#if SIMDJSON_SUPPORTS_CONCEPTS +#define SIMDJSON_SUPPORTS_DESERIALIZATION 1 +#else +#define SIMDJSON_SUPPORTS_DESERIALIZATION 0 +#endif + + +#if !defined(SIMDJSON_CONSTEVAL) +#if defined(__cpp_consteval) && __cpp_consteval >= 201811L && defined(__cpp_lib_constexpr_string) && __cpp_lib_constexpr_string >= 201907L +#define SIMDJSON_CONSTEVAL 1 +#else +#define SIMDJSON_CONSTEVAL 0 +#endif // defined(__cpp_consteval) && __cpp_consteval >= 201811L && defined(__cpp_lib_constexpr_string) && __cpp_lib_constexpr_string >= 201907L +#endif // !defined(SIMDJSON_CONSTEVAL) + #endif // SIMDJSON_COMPILER_CHECK_H /* end file simdjson/compiler_check.h */ /* including simdjson/portability.h: #include "simdjson/portability.h" */ @@ -152,6 +193,22 @@ using std::size_t; #define SIMDJSON_IS_ARM64 1 #elif defined(__riscv) && __riscv_xlen == 64 #define SIMDJSON_IS_RISCV64 1 + #if __riscv_v_intrinsic >= 11000 + #define SIMDJSON_HAS_RVV_INTRINSICS 1 + #endif + + #define SIMDJSON_HAS_ZVBB_INTRINSICS \ + 0 // there is currently no way to detect this + + #if SIMDJSON_HAS_RVV_INTRINSICS && __riscv_vector && \ + __riscv_v_min_vlen >= 128 && __riscv_v_elen >= 64 + // RISC-V V extension + #define SIMDJSON_IS_RVV 1 + #if SIMDJSON_HAS_ZVBB_INTRINSICS && __riscv_zvbb >= 1000000 + // RISC-V Vector Basic Bit-manipulation + #define SIMDJSON_IS_ZVBB 1 + #endif + #endif #elif defined(__loongarch_lp64) #define SIMDJSON_IS_LOONGARCH64 1 #elif defined(__PPC64__) || defined(_M_PPC64) @@ -298,6 +355,7 @@ using std::size_t; #if defined(NDEBUG) || defined(__OPTIMIZE__) || (defined(_MSC_VER) && !defined(_DEBUG)) // If NDEBUG is set, or __OPTIMIZE__ is set, or we are under MSVC in release mode, // then do away with asserts and use __assume. +// We still recommend that our users set NDEBUG in release mode. #if SIMDJSON_VISUAL_STUDIO #define SIMDJSON_UNREACHABLE() __assume(0) #define SIMDJSON_ASSUME(COND) __assume(COND) @@ -549,17 +607,6 @@ double from_chars(const char *first, const char* end) noexcept; // We assume by default static linkage #define SIMDJSON_DLLIMPORTEXPORT #endif - -/** - * Workaround for the vcpkg package manager. Only vcpkg should - * ever touch the next line. The SIMDJSON_USING_LIBRARY macro is otherwise unused. - */ -#if SIMDJSON_USING_LIBRARY -#define SIMDJSON_DLLIMPORTEXPORT __declspec(dllimport) -#endif -/** - * End of workaround for the vcpkg package manager. - */ #else #define SIMDJSON_DLLIMPORTEXPORT #endif @@ -1022,9 +1069,9 @@ using std::operator<<; #endif #if nssv_HAVE_NODISCARD -# define nssv_nodiscard [[nodiscard]] +# define nssv_nodiscard simdjson_warn_unused #else -# define nssv_nodiscard /*[[nodiscard]]*/ +# define nssv_nodiscard /*simdjson_warn_unused*/ #endif // Additional includes: @@ -2342,16 +2389,25 @@ namespace std { // It could also wrongly set SIMDJSON_DEVELOPMENT_CHECKS (e.g., if the programmer // sets _DEBUG in a release build under Visual Studio, or if some compiler fails to // set the __OPTIMIZE__ macro). +// We make it so that if NDEBUG is defined, then SIMDJSON_DEVELOPMENT_CHECKS +// is not defined, irrespective of the compiler. +// We recommend that users set NDEBUG in release builds, so that +// SIMDJSON_DEVELOPMENT_CHECKS is not defined in release builds by default, +// irrespective of the compiler. #ifndef SIMDJSON_DEVELOPMENT_CHECKS #ifdef _MSC_VER // Visual Studio seems to set _DEBUG for debug builds. -#ifdef _DEBUG +// We set SIMDJSON_DEVELOPMENT_CHECKS to 1 if _DEBUG is defined +// and NDEBUG is not defined. +#if defined(_DEBUG) && !defined(NDEBUG) #define SIMDJSON_DEVELOPMENT_CHECKS 1 #endif // _DEBUG #else // _MSC_VER // All other compilers appear to set __OPTIMIZE__ to a positive integer // when the compiler is optimizing. -#ifndef __OPTIMIZE__ +// We only set SIMDJSON_DEVELOPMENT_CHECKS if both __OPTIMIZE__ +// and NDEBUG are not defined. +#if !defined(__OPTIMIZE__) && !defined(NDEBUG) #define SIMDJSON_DEVELOPMENT_CHECKS 1 #endif // __OPTIMIZE__ #endif // _MSC_VER @@ -2407,6 +2463,18 @@ namespace std { #define SIMDJSON_AVX512_ALLOWED 1 #endif + +#ifndef __has_cpp_attribute +#define simdjson_lifetime_bound +#elif __has_cpp_attribute(msvc::lifetimebound) +#define simdjson_lifetime_bound [[msvc::lifetimebound]] +#elif __has_cpp_attribute(clang::lifetimebound) +#define simdjson_lifetime_bound [[clang::lifetimebound]] +#elif __has_cpp_attribute(lifetimebound) +#define simdjson_lifetime_bound [[lifetimebound]] +#else +#define simdjson_lifetime_bound +#endif #endif // SIMDJSON_COMMON_DEFS_H /* end file simdjson/common_defs.h */ /* skipped duplicate #include "simdjson/compiler_check.h" */ @@ -2463,7 +2531,8 @@ enum error_code { SCALAR_DOCUMENT_AS_VALUE, ///< A scalar document is treated as a value. OUT_OF_BOUNDS, ///< Attempted to access location outside of document. TRAILING_CONTENT, ///< Unexpected trailing content in the JSON input - NUM_ERROR_CODES + OUT_OF_CAPACITY, ///< The capacity was exceeded, we cannot allocate enough memory. + NUM_ERROR_CODES ///< Placeholder for end of error code list. }; /** @@ -2521,6 +2590,10 @@ namespace internal { /** * The result of a simdjson operation that could fail. * + * IMPORTANT: For the ondemand API, we use implementation_simdjson_result_base as a base class + * to avoid some compilation issue. Thus, if you modify this class, please ensure that the ondemand + * implementation_simdjson_result_base is also modified. + * * Gives the option of reading error codes, or throwing an exception by casting to the desired result. * * This is a base class for implementations that want to add functions to the result type for @@ -2581,8 +2654,27 @@ struct simdjson_result_base : protected std::pair { */ simdjson_inline error_code error() const noexcept; + /** + * Whether there is a value. + */ + simdjson_inline bool has_value() const noexcept; #if SIMDJSON_EXCEPTIONS + /** + * Dereference operator to access the contained value. + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T& operator*() & noexcept(false); + simdjson_inline T&& operator*() && noexcept(false); + /** + * Arrow operator to access members of the contained value. + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T* operator->() noexcept(false); + simdjson_inline const T* operator->() const noexcept(false); + /** * Get the result value. * @@ -2616,12 +2708,42 @@ struct simdjson_result_base : protected std::pair { /** * Get the result value. This function is safe if and only * the error() method returns a value that evaluates to false. + * We discourage the use of value_unsafe(). + * + * The recommended pattern is: + * + * T value; // where T is the type + * auto error = result.get(value); + * if (error) { + * // handle error + * } + * + * Or you may call 'value()' which will raise an exception + * in case of error: + * + * T value = result.value(); */ simdjson_inline const T& value_unsafe() const& noexcept; /** * Take the result value (move it). This function is safe if and only * the error() method returns a value that evaluates to false. + * We discourage the use of value_unsafe(). + * + * The recommended pattern is: + * + * T value; // where T is the type + * auto error = result.get(value); + * if (error) { + * // handle error, return, exit, abort + * } else { + * // use value here. + * } + * + * Or you may call 'value()' which will raise an exception + * in case of error: + * + * T value = result.value(); */ simdjson_inline T&& value_unsafe() && noexcept; @@ -2636,6 +2758,7 @@ struct simdjson_result_base : protected std::pair { */ template struct simdjson_result : public internal::simdjson_result_base { + /** * @private Create a new empty result with error = UNINITIALIZED. */ @@ -2667,24 +2790,33 @@ struct simdjson_result : public internal::simdjson_result_base { * @param value The variable to assign the value to. May not be set if there is an error. */ simdjson_warn_unused simdjson_inline error_code get(T &value) && noexcept; -// + /** * Copy the value to a provided std::string, only enabled for std::string_view. * * @param value The variable to assign the value to. May not be set if there is an error. */ - simdjson_warn_unused simdjson_inline error_code get(std::string &value) && noexcept -#if SIMDJSON_SUPPORTS_DESERIALIZATION - requires (!std::is_same_v) -#endif // SIMDJSON_SUPPORTS_DESERIALIZATION - ; + template + simdjson_warn_unused simdjson_inline error_code get(std::string &value) && noexcept { + static_assert(std::is_same::value, "SFINAE"); + std::string_view v; + error_code error = std::forward>(*this).get(v); + if (!error) { + value.assign(v.data(), v.size()); + } + return error; + } + /** * The error. */ simdjson_inline error_code error() const noexcept; -#if SIMDJSON_EXCEPTIONS + +#if SIMDJSON_EXCEPTIONS + using internal::simdjson_result_base::operator*; + using internal::simdjson_result_base::operator->; /** * Get the result value. * @@ -2755,7 +2887,7 @@ inline const std::string error_message(int error) noexcept; /* begin file simdjson/concepts.h */ #ifndef SIMDJSON_CONCEPTS_H #define SIMDJSON_CONCEPTS_H -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS #include #include @@ -2787,7 +2919,9 @@ SIMDJSON_IMPL_CONCEPT(op_append, operator+=) #undef SIMDJSON_IMPL_CONCEPT } // namespace details - +template +concept is_pair = requires { typename T::first_type; typename T::second_type; } && + std::same_as>; template concept string_view_like = std::is_convertible_v && !std::is_convertible_v; @@ -2870,21 +3004,133 @@ concept optional_type = requires(std::remove_cvref_t obj) { { obj.value() } -> std::same_as::value_type&>; requires requires(typename std::remove_cvref_t::value_type &&val) { obj.emplace(std::move(val)); - obj = std::move(val); { obj.value_or(val) } -> std::convertible_to::value_type>; }; { static_cast(obj) } -> std::same_as; // convertible to bool + { obj.reset() } noexcept -> std::same_as; }; +// Types we serialize as JSON strings (not as containers) +template +concept string_like = + std::is_same_v, std::string> || + std::is_same_v, std::string_view> || + std::is_same_v, const char*> || + std::is_same_v, char*>; + +// Concept that checks if a type is a container but not a string (because +// strings handling must be handled differently) +// Now uses iterator-based approach for broader container support +template +concept container_but_not_string = + std::ranges::input_range && !string_like && !concepts::string_view_keyed_map; + + } // namespace concepts + + +/** + * We use tag_invoke as our customization point mechanism. + */ +template +concept tag_invocable = requires(Tag tag, Args... args) { + tag_invoke(std::forward(tag), std::forward(args)...); +}; + +template +concept nothrow_tag_invocable = + tag_invocable && requires(Tag tag, Args... args) { + { + tag_invoke(std::forward(tag), std::forward(args)...) + } noexcept; + }; + } // namespace simdjson -#endif // SIMDJSON_SUPPORTS_DESERIALIZATION +#endif // SIMDJSON_SUPPORTS_CONCEPTS #endif // SIMDJSON_CONCEPTS_H /* end file simdjson/concepts.h */ +/* including simdjson/constevalutil.h: #include "simdjson/constevalutil.h" */ +/* begin file simdjson/constevalutil.h */ +#ifndef SIMDJSON_CONSTEVALUTIL_H +#define SIMDJSON_CONSTEVALUTIL_H + +#include +#include +#include + +namespace simdjson { +namespace constevalutil { +#if SIMDJSON_CONSTEVAL + +constexpr static std::array json_quotable_character = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 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, 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, 1, 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, 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, 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, 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, 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, 0, 0, 0}; +constexpr static std::array control_chars = { + "\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", "\\u0006", + "\\u0007", "\\b", "\\t", "\\n", "\\u000b", "\\f", "\\r", + "\\u000e", "\\u000f", "\\u0010", "\\u0011", "\\u0012", "\\u0013", "\\u0014", + "\\u0015", "\\u0016", "\\u0017", "\\u0018", "\\u0019", "\\u001a", "\\u001b", + "\\u001c", "\\u001d", "\\u001e", "\\u001f"}; +// unoptimized, meant for compile-time execution +consteval std::string consteval_to_quoted_escaped(std::string_view input) { + std::string out = "\""; + for (char c : input) { + if (json_quotable_character[uint8_t(c)]) { + if (c == '"') { + out.append("\\\""); + } else if (c == '\\') { + out.append("\\\\"); + } else { + std::string_view v = control_chars[uint8_t(c)]; + out.append(v); + } + } else { + out.push_back(c); + } + } + out.push_back('"'); + return out; +} +#endif // SIMDJSON_CONSTEVAL + + +#if SIMDJSON_SUPPORTS_CONCEPTS +template +struct fixed_string { + constexpr fixed_string(const char (&str)[N]) { + for (std::size_t i = 0; i < N; ++i) { + data[i] = str[i]; + } + } + char data[N]; + constexpr std::string_view view() const { return {data, N - 1}; } +}; +template +fixed_string(const char (&)[N]) -> fixed_string; + +template +struct string_constant { + static constexpr std::string_view value = str.view(); +}; +#endif // SIMDJSON_SUPPORTS_CONCEPTS + +} // namespace constevalutil +} // namespace simdjson +#endif // SIMDJSON_CONSTEVALUTIL_H +/* end file simdjson/constevalutil.h */ /** * @brief The top level simdjson namespace, containing everything the library provides. @@ -4612,8 +4858,38 @@ simdjson_inline error_code simdjson_result_base::error() const noexcept { return this->second; } + +template +simdjson_inline bool simdjson_result_base::has_value() const noexcept { + return this->error() == SUCCESS; +} + #if SIMDJSON_EXCEPTIONS + +template +simdjson_inline T& simdjson_result_base::operator*() & noexcept(false) { + return this->value(); +} + +template +simdjson_inline T&& simdjson_result_base::operator*() && noexcept(false) { + return std::forward>(*this).value(); +} + +template +simdjson_inline T* simdjson_result_base::operator->() noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + + +template +simdjson_inline const T* simdjson_result_base::operator->() const noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + template simdjson_inline T& simdjson_result_base::value() & noexcept(false) { if (error()) { throw simdjson_error(error()); } @@ -4638,6 +4914,7 @@ simdjson_inline simdjson_result_base::operator T&&() && noexcept(false) { #endif // SIMDJSON_EXCEPTIONS + template simdjson_inline const T& simdjson_result_base::value_unsafe() const& noexcept { return this->first; @@ -4672,26 +4949,10 @@ simdjson_inline void simdjson_result::tie(T &value, error_code &error) && noe std::forward>(*this).tie(value, error); } -template -simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &value) && noexcept { - return std::forward>(*this).get(value); -} - template simdjson_warn_unused simdjson_inline error_code -simdjson_result::get(std::string &value) && noexcept -#if SIMDJSON_SUPPORTS_DESERIALIZATION -requires (!std::is_same_v) -#endif // SIMDJSON_SUPPORTS_DESERIALIZATION -{ - // SFINAE : n'active que pour T = std::string_view - static_assert(std::is_same::value, "simdjson_result::get(std::string&) n'est disponible que pour T = std::string_view"); - std::string_view v; - error_code error = std::forward>(*this).get(v); - if (!error) { - value.assign(v.data(), v.size()); - } - return error; +simdjson_result::get(T &value) && noexcept { + return std::forward>(*this).get(value); } template @@ -8209,6 +8470,12 @@ namespace { tmp = vpaddq_u8(tmp, tmp); return vgetq_lane_u16(vreinterpretq_u16_u8(tmp), 0); } + // Returns 4-bit out of each byte, alternating between the high 4 bits and low + // bits result it is 64 bit. + simdjson_inline uint64_t to_bitmask64() const { + return vget_lane_u64( + vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(*this), 4)), 0); + } simdjson_inline bool any() const { return vmaxvq_u32(vreinterpretq_u32_u8(*this)) != 0; } }; @@ -8285,7 +8552,7 @@ namespace { // Bit-specific operations simdjson_inline simd8 any_bits_set(simd8 bits) const { return vtstq_u8(*this, bits); } - simdjson_inline bool any_bits_set_anywhere() const { return this->max_val() != 0; } + simdjson_inline bool any_bits_set_anywhere() const { return vmaxvq_u32(vreinterpretq_u32_u8(*this)) != 0; } simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { return (*this & bits).any_bits_set_anywhere(); } template simdjson_inline simd8 shr() const { return vshrq_n_u8(*this, N); } @@ -8298,7 +8565,12 @@ namespace { return lookup_table.apply_lookup_16_to(*this); } - + // Returns 4-bit out of each byte, alternating between the high 4 bits and low + // bits result it is 64 bit. + simdjson_inline uint64_t to_bitmask64() const { + return vget_lane_u64( + vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(*this), 4)), 0); + } // Copies to 'output" all bytes corresponding to a 0 in the mask (interpreted as a bitset). // Passing a 0 value for mask would be equivalent to writing out every byte to output. // Only the first 16 - count_ones(mask) bytes of the result are significant but 16 bytes @@ -8592,7 +8864,7 @@ using namespace simd; struct backslash_and_quote { public: static constexpr uint32_t BYTES_PROCESSED = 32; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); + simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; } simdjson_inline bool has_backslash() { return bs_bits != 0; } @@ -8621,6 +8893,32 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin }; } +struct escaping { + static constexpr uint32_t BYTES_PROCESSED = 16; + simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst); + + simdjson_inline bool has_escape() { return escape_bits != 0; } + simdjson_inline int escape_index() { return trailing_zeroes(escape_bits) / 4; } + + uint64_t escape_bits; +}; // struct escaping + + + +simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) { + static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes"); + simd8 v(src); + v.store(dst); + simd8 is_quote = (v == '"'); + simd8 is_backslash = (v == '\\'); + simd8 is_control = (v < 32); + return { + (is_backslash | is_quote | is_control).to_bitmask64() + }; +} + + + } // unnamed namespace } // namespace arm64 } // namespace simdjson @@ -9040,12 +9338,17 @@ struct implementation_simdjson_result_base { * * @param value The variable to assign the value to. May not be set if there is an error. */ - simdjson_inline error_code get(T &value) && noexcept; + simdjson_warn_unused simdjson_inline error_code get(T &value) && noexcept; /** * The error. */ - simdjson_inline error_code error() const noexcept; + simdjson_warn_unused simdjson_inline error_code error() const noexcept; + + /** + * Whether there is a value. + */ + simdjson_warn_unused simdjson_inline bool has_value() const noexcept; #if SIMDJSON_EXCEPTIONS @@ -9054,6 +9357,16 @@ struct implementation_simdjson_result_base { * * @throw simdjson_error if there was an error. */ + simdjson_inline T& operator*() & noexcept(false); + simdjson_inline T&& operator*() && noexcept(false); + /** + * Arrow operator to access members of the contained value. + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T* operator->() noexcept(false); + simdjson_inline const T* operator->() const noexcept(false); + simdjson_inline T& value() & noexcept(false); /** @@ -9095,6 +9408,7 @@ struct implementation_simdjson_result_base { * the error() method returns a value that evaluates to false. */ simdjson_inline T&& value_unsafe() && noexcept; + protected: /** users should never directly access first and second. **/ T first{}; /** Users should never directly access 'first'. **/ @@ -9471,7 +9785,7 @@ simdjson_inline bool is_digit(const uint8_t c) { return static_cast(c - '0') <= 9; } -simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const uint8_t *const src, const uint8_t *&p, uint64_t &i, int64_t &exponent) { +simdjson_warn_unused simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const uint8_t *const src, const uint8_t *&p, uint64_t &i, int64_t &exponent) { // we continue with the fiction that we have an integer. If the // floating point number is representable as x * 10^z for some integer // z that fits in 53 bits, then we will be able to convert back the @@ -9499,7 +9813,7 @@ simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const u return SUCCESS; } -simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const src, const uint8_t *&p, int64_t &exponent) { +simdjson_warn_unused simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const src, const uint8_t *&p, int64_t &exponent) { // Exp Sign: -123.456e[-]78 bool neg_exp = ('-' == *p); if (neg_exp || '+' == *p) { p++; } // Skip + as well @@ -9588,7 +9902,7 @@ static error_code slow_float_parsing(simdjson_unused const uint8_t * src, double /** @private */ template -simdjson_inline error_code write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer) { +simdjson_warn_unused simdjson_inline error_code write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer) { // If we frequently had to deal with long strings of digits, // we could extend our code by using a 128-bit integer instead // of a 64-bit integer. However, this is uncommon in practice. @@ -9651,13 +9965,13 @@ simdjson_inline error_code write_float(const uint8_t *const src, bool negative, // // Our objective is accurate parsing (ULP of 0) at high speed. template -simdjson_inline error_code parse_number(const uint8_t *const src, W &writer); +simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const src, W &writer); // for performance analysis, it is sometimes useful to skip parsing #ifdef SIMDJSON_SKIPNUMBERPARSING template -simdjson_inline error_code parse_number(const uint8_t *const, W &writer) { +simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const, W &writer) { writer.append_s64(0); // always write zero return SUCCESS; // always succeeds } @@ -9683,7 +9997,7 @@ simdjson_unused simdjson_inline simdjson_result get_number_type(con // // Our objective is accurate parsing (ULP of 0) at high speed. template -simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { +simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { // // Check for minus sign // @@ -10467,12 +10781,41 @@ simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_b } template -simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { +simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { return this->second; } + +template +simdjson_warn_unused simdjson_inline bool implementation_simdjson_result_base::has_value() const noexcept { + return this->error() == SUCCESS; +} + #if SIMDJSON_EXCEPTIONS +template +simdjson_inline T& implementation_simdjson_result_base::operator*() & noexcept(false) { + return this->value(); +} + +template +simdjson_inline T&& implementation_simdjson_result_base::operator*() && noexcept(false) { + return std::forward>(*this).value(); +} + +template +simdjson_inline T* implementation_simdjson_result_base::operator->() noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + + +template +simdjson_inline const T* implementation_simdjson_result_base::operator->() const noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + template simdjson_inline T& implementation_simdjson_result_base::value() & noexcept(false) { if (error()) { throw simdjson_error(error()); } @@ -10991,6 +11334,12 @@ namespace { tmp = vpaddq_u8(tmp, tmp); return vgetq_lane_u16(vreinterpretq_u16_u8(tmp), 0); } + // Returns 4-bit out of each byte, alternating between the high 4 bits and low + // bits result it is 64 bit. + simdjson_inline uint64_t to_bitmask64() const { + return vget_lane_u64( + vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(*this), 4)), 0); + } simdjson_inline bool any() const { return vmaxvq_u32(vreinterpretq_u32_u8(*this)) != 0; } }; @@ -11067,7 +11416,7 @@ namespace { // Bit-specific operations simdjson_inline simd8 any_bits_set(simd8 bits) const { return vtstq_u8(*this, bits); } - simdjson_inline bool any_bits_set_anywhere() const { return this->max_val() != 0; } + simdjson_inline bool any_bits_set_anywhere() const { return vmaxvq_u32(vreinterpretq_u32_u8(*this)) != 0; } simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { return (*this & bits).any_bits_set_anywhere(); } template simdjson_inline simd8 shr() const { return vshrq_n_u8(*this, N); } @@ -11080,7 +11429,12 @@ namespace { return lookup_table.apply_lookup_16_to(*this); } - + // Returns 4-bit out of each byte, alternating between the high 4 bits and low + // bits result it is 64 bit. + simdjson_inline uint64_t to_bitmask64() const { + return vget_lane_u64( + vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(*this), 4)), 0); + } // Copies to 'output" all bytes corresponding to a 0 in the mask (interpreted as a bitset). // Passing a 0 value for mask would be equivalent to writing out every byte to output. // Only the first 16 - count_ones(mask) bytes of the result are significant but 16 bytes @@ -11374,7 +11728,7 @@ using namespace simd; struct backslash_and_quote { public: static constexpr uint32_t BYTES_PROCESSED = 32; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); + simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; } simdjson_inline bool has_backslash() { return bs_bits != 0; } @@ -11403,6 +11757,32 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin }; } +struct escaping { + static constexpr uint32_t BYTES_PROCESSED = 16; + simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst); + + simdjson_inline bool has_escape() { return escape_bits != 0; } + simdjson_inline int escape_index() { return trailing_zeroes(escape_bits) / 4; } + + uint64_t escape_bits; +}; // struct escaping + + + +simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) { + static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes"); + simd8 v(src); + v.store(dst); + simd8 is_quote = (v == '"'); + simd8 is_backslash = (v == '\\'); + simd8 is_control = (v < 32); + return { + (is_backslash | is_quote | is_control).to_bitmask64() + }; +} + + + } // unnamed namespace } // namespace arm64 } // namespace simdjson @@ -12110,7 +12490,7 @@ using namespace simd; } } // do not forget to call check_eof! - simdjson_inline error_code errors() { + simdjson_warn_unused simdjson_inline error_code errors() { return this->error.any_bits_set_anywhere() ? error_code::UTF8_ERROR : error_code::SUCCESS; } @@ -12235,7 +12615,7 @@ class json_scanner { json_scanner() = default; simdjson_inline json_block next(const simd::simd8x64& in); // Returns either UNCLOSED_STRING or SUCCESS - simdjson_inline error_code finish(); + simdjson_warn_unused simdjson_inline error_code finish(); private: // Whether the last character of the previous iteration is part of a scalar token @@ -12283,7 +12663,7 @@ simdjson_inline json_block json_scanner::next(const simd::simd8x64& in) ); } -simdjson_inline error_code json_scanner::finish() { +simdjson_warn_unused simdjson_inline error_code json_scanner::finish() { return string_scanner.finish(); } @@ -12437,7 +12817,7 @@ class json_minifier { template simdjson_inline void step(const uint8_t *block_buf, buf_block_reader &reader) noexcept; simdjson_inline void next(const simd::simd8x64& in, const json_block& block); - simdjson_inline error_code finish(uint8_t *dst_start, size_t &dst_len); + simdjson_warn_unused simdjson_inline error_code finish(uint8_t *dst_start, size_t &dst_len); json_scanner scanner{}; uint8_t *dst; }; @@ -12447,7 +12827,7 @@ simdjson_inline void json_minifier::next(const simd::simd8x64& in, cons dst += in.compress(mask, dst); } -simdjson_inline error_code json_minifier::finish(uint8_t *dst_start, size_t &dst_len) { +simdjson_warn_unused simdjson_inline error_code json_minifier::finish(uint8_t *dst_start, size_t &dst_len) { error_code error = scanner.finish(); if (error) { dst_len = 0; return error; } dst_len = dst - dst_start; @@ -12656,7 +13036,7 @@ class json_structural_indexer { template simdjson_inline void step(const uint8_t *block, buf_block_reader &reader) noexcept; simdjson_inline void next(const simd::simd8x64& in, const json_block& block, size_t idx); - simdjson_inline error_code finish(dom_parser_implementation &parser, size_t idx, size_t len, stage1_mode partial); + simdjson_warn_unused simdjson_inline error_code finish(dom_parser_implementation &parser, size_t idx, size_t len, stage1_mode partial); json_scanner scanner{}; utf8_checker checker{}; @@ -13508,6 +13888,7 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::visit_primitive(V /* end file generic/stage2/json_iterator.h for arm64 */ /* including generic/stage2/stringparsing.h for arm64: #include */ /* begin file generic/stage2/stringparsing.h for arm64 */ +#include #ifndef SIMDJSON_SRC_GENERIC_STAGE2_STRINGPARSING_H /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ @@ -13660,7 +14041,8 @@ simdjson_inline bool handle_unicode_codepoint_wobbly(const uint8_t **src_ptr, simdjson_warn_unused simdjson_inline uint8_t *parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) { while (1) { // Copy the next n bytes, and find the backslash and quote in them. - auto bs_quote = backslash_and_quote::copy_and_find(src, dst); + auto b = backslash_and_quote{}; + auto bs_quote = b.copy_and_find(src, dst); // If the next thing is the end quote, copy and return if (bs_quote.has_quote_first()) { // we encountered quotes first. Move dst to point to quotes and exit @@ -13705,7 +14087,8 @@ simdjson_warn_unused simdjson_inline uint8_t *parse_wobbly_string(const uint8_t // It is not ideal that this function is nearly identical to parse_string. while (1) { // Copy the next n bytes, and find the backslash and quote in them. - auto bs_quote = backslash_and_quote::copy_and_find(src, dst); + auto b = backslash_and_quote{}; + auto bs_quote = b.copy_and_find(src, dst); // If the next thing is the end quote, copy and return if (bs_quote.has_quote_first()) { // we encountered quotes first. Move dst to point to quotes and exit @@ -13747,6 +14130,7 @@ simdjson_warn_unused simdjson_inline uint8_t *parse_wobbly_string(const uint8_t } } // namespace stringparsing + } // unnamed namespace } // namespace arm64 } // namespace simdjson @@ -14974,7 +15358,7 @@ using namespace simd; struct backslash_and_quote { public: static constexpr uint32_t BYTES_PROCESSED = 32; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); + simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; } simdjson_inline bool has_backslash() { return ((quote_bits - 1) & bs_bits) != 0; } @@ -14998,6 +15382,31 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin }; } + +struct escaping { + static constexpr uint32_t BYTES_PROCESSED = 32; + simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst); + + simdjson_inline bool has_escape() { return escape_bits != 0; } + simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); } + + uint64_t escape_bits; +}; // struct escaping + + + +simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) { + static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes"); + simd8 v(src); + v.store(dst); + simd8 is_quote = (v == '"'); + simd8 is_backslash = (v == '\\'); + simd8 is_control = (v < 32); + return { + uint64_t((is_backslash | is_quote | is_control).to_bitmask()) + }; +} + } // unnamed namespace } // namespace haswell } // namespace simdjson @@ -15415,12 +15824,17 @@ struct implementation_simdjson_result_base { * * @param value The variable to assign the value to. May not be set if there is an error. */ - simdjson_inline error_code get(T &value) && noexcept; + simdjson_warn_unused simdjson_inline error_code get(T &value) && noexcept; /** * The error. */ - simdjson_inline error_code error() const noexcept; + simdjson_warn_unused simdjson_inline error_code error() const noexcept; + + /** + * Whether there is a value. + */ + simdjson_warn_unused simdjson_inline bool has_value() const noexcept; #if SIMDJSON_EXCEPTIONS @@ -15429,6 +15843,16 @@ struct implementation_simdjson_result_base { * * @throw simdjson_error if there was an error. */ + simdjson_inline T& operator*() & noexcept(false); + simdjson_inline T&& operator*() && noexcept(false); + /** + * Arrow operator to access members of the contained value. + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T* operator->() noexcept(false); + simdjson_inline const T* operator->() const noexcept(false); + simdjson_inline T& value() & noexcept(false); /** @@ -15470,6 +15894,7 @@ struct implementation_simdjson_result_base { * the error() method returns a value that evaluates to false. */ simdjson_inline T&& value_unsafe() && noexcept; + protected: /** users should never directly access first and second. **/ T first{}; /** Users should never directly access 'first'. **/ @@ -15846,7 +16271,7 @@ simdjson_inline bool is_digit(const uint8_t c) { return static_cast(c - '0') <= 9; } -simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const uint8_t *const src, const uint8_t *&p, uint64_t &i, int64_t &exponent) { +simdjson_warn_unused simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const uint8_t *const src, const uint8_t *&p, uint64_t &i, int64_t &exponent) { // we continue with the fiction that we have an integer. If the // floating point number is representable as x * 10^z for some integer // z that fits in 53 bits, then we will be able to convert back the @@ -15874,7 +16299,7 @@ simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const u return SUCCESS; } -simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const src, const uint8_t *&p, int64_t &exponent) { +simdjson_warn_unused simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const src, const uint8_t *&p, int64_t &exponent) { // Exp Sign: -123.456e[-]78 bool neg_exp = ('-' == *p); if (neg_exp || '+' == *p) { p++; } // Skip + as well @@ -15963,7 +16388,7 @@ static error_code slow_float_parsing(simdjson_unused const uint8_t * src, double /** @private */ template -simdjson_inline error_code write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer) { +simdjson_warn_unused simdjson_inline error_code write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer) { // If we frequently had to deal with long strings of digits, // we could extend our code by using a 128-bit integer instead // of a 64-bit integer. However, this is uncommon in practice. @@ -16026,13 +16451,13 @@ simdjson_inline error_code write_float(const uint8_t *const src, bool negative, // // Our objective is accurate parsing (ULP of 0) at high speed. template -simdjson_inline error_code parse_number(const uint8_t *const src, W &writer); +simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const src, W &writer); // for performance analysis, it is sometimes useful to skip parsing #ifdef SIMDJSON_SKIPNUMBERPARSING template -simdjson_inline error_code parse_number(const uint8_t *const, W &writer) { +simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const, W &writer) { writer.append_s64(0); // always write zero return SUCCESS; // always succeeds } @@ -16058,7 +16483,7 @@ simdjson_unused simdjson_inline simdjson_result get_number_type(con // // Our objective is accurate parsing (ULP of 0) at high speed. template -simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { +simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { // // Check for minus sign // @@ -16842,12 +17267,41 @@ simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_b } template -simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { +simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { return this->second; } + +template +simdjson_warn_unused simdjson_inline bool implementation_simdjson_result_base::has_value() const noexcept { + return this->error() == SUCCESS; +} + #if SIMDJSON_EXCEPTIONS +template +simdjson_inline T& implementation_simdjson_result_base::operator*() & noexcept(false) { + return this->value(); +} + +template +simdjson_inline T&& implementation_simdjson_result_base::operator*() && noexcept(false) { + return std::forward>(*this).value(); +} + +template +simdjson_inline T* implementation_simdjson_result_base::operator->() noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + + +template +simdjson_inline const T* implementation_simdjson_result_base::operator->() const noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + template simdjson_inline T& implementation_simdjson_result_base::value() & noexcept(false) { if (error()) { throw simdjson_error(error()); } @@ -17627,7 +18081,7 @@ using namespace simd; struct backslash_and_quote { public: static constexpr uint32_t BYTES_PROCESSED = 32; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); + simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; } simdjson_inline bool has_backslash() { return ((quote_bits - 1) & bs_bits) != 0; } @@ -17651,6 +18105,31 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin }; } + +struct escaping { + static constexpr uint32_t BYTES_PROCESSED = 32; + simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst); + + simdjson_inline bool has_escape() { return escape_bits != 0; } + simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); } + + uint64_t escape_bits; +}; // struct escaping + + + +simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) { + static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes"); + simd8 v(src); + v.store(dst); + simd8 is_quote = (v == '"'); + simd8 is_backslash = (v == '\\'); + simd8 is_control = (v < 32); + return { + uint64_t((is_backslash | is_quote | is_control).to_bitmask()) + }; +} + } // unnamed namespace } // namespace haswell } // namespace simdjson @@ -18356,7 +18835,7 @@ using namespace simd; } } // do not forget to call check_eof! - simdjson_inline error_code errors() { + simdjson_warn_unused simdjson_inline error_code errors() { return this->error.any_bits_set_anywhere() ? error_code::UTF8_ERROR : error_code::SUCCESS; } @@ -18481,7 +18960,7 @@ class json_scanner { json_scanner() = default; simdjson_inline json_block next(const simd::simd8x64& in); // Returns either UNCLOSED_STRING or SUCCESS - simdjson_inline error_code finish(); + simdjson_warn_unused simdjson_inline error_code finish(); private: // Whether the last character of the previous iteration is part of a scalar token @@ -18529,7 +19008,7 @@ simdjson_inline json_block json_scanner::next(const simd::simd8x64& in) ); } -simdjson_inline error_code json_scanner::finish() { +simdjson_warn_unused simdjson_inline error_code json_scanner::finish() { return string_scanner.finish(); } @@ -18683,7 +19162,7 @@ class json_minifier { template simdjson_inline void step(const uint8_t *block_buf, buf_block_reader &reader) noexcept; simdjson_inline void next(const simd::simd8x64& in, const json_block& block); - simdjson_inline error_code finish(uint8_t *dst_start, size_t &dst_len); + simdjson_warn_unused simdjson_inline error_code finish(uint8_t *dst_start, size_t &dst_len); json_scanner scanner{}; uint8_t *dst; }; @@ -18693,7 +19172,7 @@ simdjson_inline void json_minifier::next(const simd::simd8x64& in, cons dst += in.compress(mask, dst); } -simdjson_inline error_code json_minifier::finish(uint8_t *dst_start, size_t &dst_len) { +simdjson_warn_unused simdjson_inline error_code json_minifier::finish(uint8_t *dst_start, size_t &dst_len) { error_code error = scanner.finish(); if (error) { dst_len = 0; return error; } dst_len = dst - dst_start; @@ -18902,7 +19381,7 @@ class json_structural_indexer { template simdjson_inline void step(const uint8_t *block, buf_block_reader &reader) noexcept; simdjson_inline void next(const simd::simd8x64& in, const json_block& block, size_t idx); - simdjson_inline error_code finish(dom_parser_implementation &parser, size_t idx, size_t len, stage1_mode partial); + simdjson_warn_unused simdjson_inline error_code finish(dom_parser_implementation &parser, size_t idx, size_t len, stage1_mode partial); json_scanner scanner{}; utf8_checker checker{}; @@ -19754,6 +20233,7 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::visit_primitive(V /* end file generic/stage2/json_iterator.h for haswell */ /* including generic/stage2/stringparsing.h for haswell: #include */ /* begin file generic/stage2/stringparsing.h for haswell */ +#include #ifndef SIMDJSON_SRC_GENERIC_STAGE2_STRINGPARSING_H /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ @@ -19906,7 +20386,8 @@ simdjson_inline bool handle_unicode_codepoint_wobbly(const uint8_t **src_ptr, simdjson_warn_unused simdjson_inline uint8_t *parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) { while (1) { // Copy the next n bytes, and find the backslash and quote in them. - auto bs_quote = backslash_and_quote::copy_and_find(src, dst); + auto b = backslash_and_quote{}; + auto bs_quote = b.copy_and_find(src, dst); // If the next thing is the end quote, copy and return if (bs_quote.has_quote_first()) { // we encountered quotes first. Move dst to point to quotes and exit @@ -19951,7 +20432,8 @@ simdjson_warn_unused simdjson_inline uint8_t *parse_wobbly_string(const uint8_t // It is not ideal that this function is nearly identical to parse_string. while (1) { // Copy the next n bytes, and find the backslash and quote in them. - auto bs_quote = backslash_and_quote::copy_and_find(src, dst); + auto b = backslash_and_quote{}; + auto bs_quote = b.copy_and_find(src, dst); // If the next thing is the end quote, copy and return if (bs_quote.has_quote_first()) { // we encountered quotes first. Move dst to point to quotes and exit @@ -19993,6 +20475,7 @@ simdjson_warn_unused simdjson_inline uint8_t *parse_wobbly_string(const uint8_t } } // namespace stringparsing + } // unnamed namespace } // namespace haswell } // namespace simdjson @@ -20834,7 +21317,6 @@ namespace simd { friend simdjson_really_inline uint64_t operator==(const simd8 lhs, const simd8 rhs) { return _mm512_cmpeq_epi8_mask(lhs, rhs); } - static const int SIZE = sizeof(base::value); template @@ -21153,7 +21635,7 @@ using namespace simd; struct backslash_and_quote { public: static constexpr uint32_t BYTES_PROCESSED = 64; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); + simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; } simdjson_inline bool has_backslash() { return ((quote_bits - 1) & bs_bits) != 0; } @@ -21177,6 +21659,35 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin }; } + + +struct escaping { + static constexpr uint32_t BYTES_PROCESSED = 64; + simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst); + + simdjson_inline bool has_escape() { return escape_bits != 0; } + simdjson_inline int escape_index() { return trailing_zeroes(uint64_t(escape_bits)); } + + __mmask64 escape_bits; +}; // struct escaping + + + +simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) { + static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes"); + simd8 v(src); + v.store(dst); + __mmask64 is_quote = _mm512_cmpeq_epi8_mask(v, _mm512_set1_epi8('"')); + __mmask64 is_backslash = _mm512_cmpeq_epi8_mask(v, _mm512_set1_epi8('\\')); + __mmask64 is_control = _mm512_cmplt_epi8_mask(v, _mm512_set1_epi8(32)); + return { + (is_backslash | is_quote | is_control) + }; +} + + + + } // unnamed namespace } // namespace icelake } // namespace simdjson @@ -21654,12 +22165,17 @@ struct implementation_simdjson_result_base { * * @param value The variable to assign the value to. May not be set if there is an error. */ - simdjson_inline error_code get(T &value) && noexcept; + simdjson_warn_unused simdjson_inline error_code get(T &value) && noexcept; /** * The error. */ - simdjson_inline error_code error() const noexcept; + simdjson_warn_unused simdjson_inline error_code error() const noexcept; + + /** + * Whether there is a value. + */ + simdjson_warn_unused simdjson_inline bool has_value() const noexcept; #if SIMDJSON_EXCEPTIONS @@ -21668,6 +22184,16 @@ struct implementation_simdjson_result_base { * * @throw simdjson_error if there was an error. */ + simdjson_inline T& operator*() & noexcept(false); + simdjson_inline T&& operator*() && noexcept(false); + /** + * Arrow operator to access members of the contained value. + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T* operator->() noexcept(false); + simdjson_inline const T* operator->() const noexcept(false); + simdjson_inline T& value() & noexcept(false); /** @@ -21709,6 +22235,7 @@ struct implementation_simdjson_result_base { * the error() method returns a value that evaluates to false. */ simdjson_inline T&& value_unsafe() && noexcept; + protected: /** users should never directly access first and second. **/ T first{}; /** Users should never directly access 'first'. **/ @@ -22085,7 +22612,7 @@ simdjson_inline bool is_digit(const uint8_t c) { return static_cast(c - '0') <= 9; } -simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const uint8_t *const src, const uint8_t *&p, uint64_t &i, int64_t &exponent) { +simdjson_warn_unused simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const uint8_t *const src, const uint8_t *&p, uint64_t &i, int64_t &exponent) { // we continue with the fiction that we have an integer. If the // floating point number is representable as x * 10^z for some integer // z that fits in 53 bits, then we will be able to convert back the @@ -22113,7 +22640,7 @@ simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const u return SUCCESS; } -simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const src, const uint8_t *&p, int64_t &exponent) { +simdjson_warn_unused simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const src, const uint8_t *&p, int64_t &exponent) { // Exp Sign: -123.456e[-]78 bool neg_exp = ('-' == *p); if (neg_exp || '+' == *p) { p++; } // Skip + as well @@ -22202,7 +22729,7 @@ static error_code slow_float_parsing(simdjson_unused const uint8_t * src, double /** @private */ template -simdjson_inline error_code write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer) { +simdjson_warn_unused simdjson_inline error_code write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer) { // If we frequently had to deal with long strings of digits, // we could extend our code by using a 128-bit integer instead // of a 64-bit integer. However, this is uncommon in practice. @@ -22265,13 +22792,13 @@ simdjson_inline error_code write_float(const uint8_t *const src, bool negative, // // Our objective is accurate parsing (ULP of 0) at high speed. template -simdjson_inline error_code parse_number(const uint8_t *const src, W &writer); +simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const src, W &writer); // for performance analysis, it is sometimes useful to skip parsing #ifdef SIMDJSON_SKIPNUMBERPARSING template -simdjson_inline error_code parse_number(const uint8_t *const, W &writer) { +simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const, W &writer) { writer.append_s64(0); // always write zero return SUCCESS; // always succeeds } @@ -22297,7 +22824,7 @@ simdjson_unused simdjson_inline simdjson_result get_number_type(con // // Our objective is accurate parsing (ULP of 0) at high speed. template -simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { +simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { // // Check for minus sign // @@ -23081,12 +23608,41 @@ simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_b } template -simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { +simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { return this->second; } + +template +simdjson_warn_unused simdjson_inline bool implementation_simdjson_result_base::has_value() const noexcept { + return this->error() == SUCCESS; +} + #if SIMDJSON_EXCEPTIONS +template +simdjson_inline T& implementation_simdjson_result_base::operator*() & noexcept(false) { + return this->value(); +} + +template +simdjson_inline T&& implementation_simdjson_result_base::operator*() && noexcept(false) { + return std::forward>(*this).value(); +} + +template +simdjson_inline T* implementation_simdjson_result_base::operator->() noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + + +template +simdjson_inline const T* implementation_simdjson_result_base::operator->() const noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + template simdjson_inline T& implementation_simdjson_result_base::value() & noexcept(false) { if (error()) { throw simdjson_error(error()); } @@ -23483,7 +24039,6 @@ namespace simd { friend simdjson_really_inline uint64_t operator==(const simd8 lhs, const simd8 rhs) { return _mm512_cmpeq_epi8_mask(lhs, rhs); } - static const int SIZE = sizeof(base::value); template @@ -23802,7 +24357,7 @@ using namespace simd; struct backslash_and_quote { public: static constexpr uint32_t BYTES_PROCESSED = 64; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); + simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; } simdjson_inline bool has_backslash() { return ((quote_bits - 1) & bs_bits) != 0; } @@ -23826,6 +24381,35 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin }; } + + +struct escaping { + static constexpr uint32_t BYTES_PROCESSED = 64; + simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst); + + simdjson_inline bool has_escape() { return escape_bits != 0; } + simdjson_inline int escape_index() { return trailing_zeroes(uint64_t(escape_bits)); } + + __mmask64 escape_bits; +}; // struct escaping + + + +simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) { + static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes"); + simd8 v(src); + v.store(dst); + __mmask64 is_quote = _mm512_cmpeq_epi8_mask(v, _mm512_set1_epi8('"')); + __mmask64 is_backslash = _mm512_cmpeq_epi8_mask(v, _mm512_set1_epi8('\\')); + __mmask64 is_control = _mm512_cmplt_epi8_mask(v, _mm512_set1_epi8(32)); + return { + (is_backslash | is_quote | is_control) + }; +} + + + + } // unnamed namespace } // namespace icelake } // namespace simdjson @@ -24591,7 +25175,7 @@ using namespace simd; } } // do not forget to call check_eof! - simdjson_inline error_code errors() { + simdjson_warn_unused simdjson_inline error_code errors() { return this->error.any_bits_set_anywhere() ? error_code::UTF8_ERROR : error_code::SUCCESS; } @@ -24716,7 +25300,7 @@ class json_scanner { json_scanner() = default; simdjson_inline json_block next(const simd::simd8x64& in); // Returns either UNCLOSED_STRING or SUCCESS - simdjson_inline error_code finish(); + simdjson_warn_unused simdjson_inline error_code finish(); private: // Whether the last character of the previous iteration is part of a scalar token @@ -24764,7 +25348,7 @@ simdjson_inline json_block json_scanner::next(const simd::simd8x64& in) ); } -simdjson_inline error_code json_scanner::finish() { +simdjson_warn_unused simdjson_inline error_code json_scanner::finish() { return string_scanner.finish(); } @@ -24918,7 +25502,7 @@ class json_minifier { template simdjson_inline void step(const uint8_t *block_buf, buf_block_reader &reader) noexcept; simdjson_inline void next(const simd::simd8x64& in, const json_block& block); - simdjson_inline error_code finish(uint8_t *dst_start, size_t &dst_len); + simdjson_warn_unused simdjson_inline error_code finish(uint8_t *dst_start, size_t &dst_len); json_scanner scanner{}; uint8_t *dst; }; @@ -24928,7 +25512,7 @@ simdjson_inline void json_minifier::next(const simd::simd8x64& in, cons dst += in.compress(mask, dst); } -simdjson_inline error_code json_minifier::finish(uint8_t *dst_start, size_t &dst_len) { +simdjson_warn_unused simdjson_inline error_code json_minifier::finish(uint8_t *dst_start, size_t &dst_len) { error_code error = scanner.finish(); if (error) { dst_len = 0; return error; } dst_len = dst - dst_start; @@ -25137,7 +25721,7 @@ class json_structural_indexer { template simdjson_inline void step(const uint8_t *block, buf_block_reader &reader) noexcept; simdjson_inline void next(const simd::simd8x64& in, const json_block& block, size_t idx); - simdjson_inline error_code finish(dom_parser_implementation &parser, size_t idx, size_t len, stage1_mode partial); + simdjson_warn_unused simdjson_inline error_code finish(dom_parser_implementation &parser, size_t idx, size_t len, stage1_mode partial); json_scanner scanner{}; utf8_checker checker{}; @@ -25989,6 +26573,7 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::visit_primitive(V /* end file generic/stage2/json_iterator.h for icelake */ /* including generic/stage2/stringparsing.h for icelake: #include */ /* begin file generic/stage2/stringparsing.h for icelake */ +#include #ifndef SIMDJSON_SRC_GENERIC_STAGE2_STRINGPARSING_H /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ @@ -26141,7 +26726,8 @@ simdjson_inline bool handle_unicode_codepoint_wobbly(const uint8_t **src_ptr, simdjson_warn_unused simdjson_inline uint8_t *parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) { while (1) { // Copy the next n bytes, and find the backslash and quote in them. - auto bs_quote = backslash_and_quote::copy_and_find(src, dst); + auto b = backslash_and_quote{}; + auto bs_quote = b.copy_and_find(src, dst); // If the next thing is the end quote, copy and return if (bs_quote.has_quote_first()) { // we encountered quotes first. Move dst to point to quotes and exit @@ -26186,7 +26772,8 @@ simdjson_warn_unused simdjson_inline uint8_t *parse_wobbly_string(const uint8_t // It is not ideal that this function is nearly identical to parse_string. while (1) { // Copy the next n bytes, and find the backslash and quote in them. - auto bs_quote = backslash_and_quote::copy_and_find(src, dst); + auto b = backslash_and_quote{}; + auto bs_quote = b.copy_and_find(src, dst); // If the next thing is the end quote, copy and return if (bs_quote.has_quote_first()) { // we encountered quotes first. Move dst to point to quotes and exit @@ -26228,6 +26815,7 @@ simdjson_warn_unused simdjson_inline uint8_t *parse_wobbly_string(const uint8_t } } // namespace stringparsing + } // unnamed namespace } // namespace icelake } // namespace simdjson @@ -27589,7 +28177,7 @@ using namespace simd; struct backslash_and_quote { public: static constexpr uint32_t BYTES_PROCESSED = 32; - simdjson_inline static backslash_and_quote + simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); simdjson_inline bool has_quote_first() { @@ -27630,6 +28218,32 @@ backslash_and_quote::copy_and_find(const uint8_t *src, uint8_t *dst) { }; } + +struct escaping { + static constexpr uint32_t BYTES_PROCESSED = 16; + simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst); + + simdjson_inline bool has_escape() { return escape_bits != 0; } + simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); } + + uint64_t escape_bits; +}; // struct escaping + + + +simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) { + static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes"); + simd8 v(src); + v.store(dst); + simd8 is_quote = (v == '"'); + simd8 is_backslash = (v == '\\'); + simd8 is_control = (v < 32); + return { + // We store it as a 64-bit bitmask even though we only need 16 bits. + uint64_t((is_backslash | is_quote | is_control).to_bitmask()) + }; +} + } // unnamed namespace } // namespace ppc64 } // namespace simdjson @@ -28049,12 +28663,17 @@ struct implementation_simdjson_result_base { * * @param value The variable to assign the value to. May not be set if there is an error. */ - simdjson_inline error_code get(T &value) && noexcept; + simdjson_warn_unused simdjson_inline error_code get(T &value) && noexcept; /** * The error. */ - simdjson_inline error_code error() const noexcept; + simdjson_warn_unused simdjson_inline error_code error() const noexcept; + + /** + * Whether there is a value. + */ + simdjson_warn_unused simdjson_inline bool has_value() const noexcept; #if SIMDJSON_EXCEPTIONS @@ -28063,6 +28682,16 @@ struct implementation_simdjson_result_base { * * @throw simdjson_error if there was an error. */ + simdjson_inline T& operator*() & noexcept(false); + simdjson_inline T&& operator*() && noexcept(false); + /** + * Arrow operator to access members of the contained value. + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T* operator->() noexcept(false); + simdjson_inline const T* operator->() const noexcept(false); + simdjson_inline T& value() & noexcept(false); /** @@ -28104,6 +28733,7 @@ struct implementation_simdjson_result_base { * the error() method returns a value that evaluates to false. */ simdjson_inline T&& value_unsafe() && noexcept; + protected: /** users should never directly access first and second. **/ T first{}; /** Users should never directly access 'first'. **/ @@ -28480,7 +29110,7 @@ simdjson_inline bool is_digit(const uint8_t c) { return static_cast(c - '0') <= 9; } -simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const uint8_t *const src, const uint8_t *&p, uint64_t &i, int64_t &exponent) { +simdjson_warn_unused simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const uint8_t *const src, const uint8_t *&p, uint64_t &i, int64_t &exponent) { // we continue with the fiction that we have an integer. If the // floating point number is representable as x * 10^z for some integer // z that fits in 53 bits, then we will be able to convert back the @@ -28508,7 +29138,7 @@ simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const u return SUCCESS; } -simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const src, const uint8_t *&p, int64_t &exponent) { +simdjson_warn_unused simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const src, const uint8_t *&p, int64_t &exponent) { // Exp Sign: -123.456e[-]78 bool neg_exp = ('-' == *p); if (neg_exp || '+' == *p) { p++; } // Skip + as well @@ -28597,7 +29227,7 @@ static error_code slow_float_parsing(simdjson_unused const uint8_t * src, double /** @private */ template -simdjson_inline error_code write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer) { +simdjson_warn_unused simdjson_inline error_code write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer) { // If we frequently had to deal with long strings of digits, // we could extend our code by using a 128-bit integer instead // of a 64-bit integer. However, this is uncommon in practice. @@ -28660,13 +29290,13 @@ simdjson_inline error_code write_float(const uint8_t *const src, bool negative, // // Our objective is accurate parsing (ULP of 0) at high speed. template -simdjson_inline error_code parse_number(const uint8_t *const src, W &writer); +simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const src, W &writer); // for performance analysis, it is sometimes useful to skip parsing #ifdef SIMDJSON_SKIPNUMBERPARSING template -simdjson_inline error_code parse_number(const uint8_t *const, W &writer) { +simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const, W &writer) { writer.append_s64(0); // always write zero return SUCCESS; // always succeeds } @@ -28692,7 +29322,7 @@ simdjson_unused simdjson_inline simdjson_result get_number_type(con // // Our objective is accurate parsing (ULP of 0) at high speed. template -simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { +simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { // // Check for minus sign // @@ -29476,12 +30106,41 @@ simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_b } template -simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { +simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { return this->second; } + +template +simdjson_warn_unused simdjson_inline bool implementation_simdjson_result_base::has_value() const noexcept { + return this->error() == SUCCESS; +} + #if SIMDJSON_EXCEPTIONS +template +simdjson_inline T& implementation_simdjson_result_base::operator*() & noexcept(false) { + return this->value(); +} + +template +simdjson_inline T&& implementation_simdjson_result_base::operator*() && noexcept(false) { + return std::forward>(*this).value(); +} + +template +simdjson_inline T* implementation_simdjson_result_base::operator->() noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + + +template +simdjson_inline const T* implementation_simdjson_result_base::operator->() const noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + template simdjson_inline T& implementation_simdjson_result_base::value() & noexcept(false) { if (error()) { throw simdjson_error(error()); } @@ -30353,7 +31012,7 @@ using namespace simd; struct backslash_and_quote { public: static constexpr uint32_t BYTES_PROCESSED = 32; - simdjson_inline static backslash_and_quote + simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); simdjson_inline bool has_quote_first() { @@ -30394,6 +31053,32 @@ backslash_and_quote::copy_and_find(const uint8_t *src, uint8_t *dst) { }; } + +struct escaping { + static constexpr uint32_t BYTES_PROCESSED = 16; + simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst); + + simdjson_inline bool has_escape() { return escape_bits != 0; } + simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); } + + uint64_t escape_bits; +}; // struct escaping + + + +simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) { + static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes"); + simd8 v(src); + v.store(dst); + simd8 is_quote = (v == '"'); + simd8 is_backslash = (v == '\\'); + simd8 is_control = (v < 32); + return { + // We store it as a 64-bit bitmask even though we only need 16 bits. + uint64_t((is_backslash | is_quote | is_control).to_bitmask()) + }; +} + } // unnamed namespace } // namespace ppc64 } // namespace simdjson @@ -31101,7 +31786,7 @@ using namespace simd; } } // do not forget to call check_eof! - simdjson_inline error_code errors() { + simdjson_warn_unused simdjson_inline error_code errors() { return this->error.any_bits_set_anywhere() ? error_code::UTF8_ERROR : error_code::SUCCESS; } @@ -31226,7 +31911,7 @@ class json_scanner { json_scanner() = default; simdjson_inline json_block next(const simd::simd8x64& in); // Returns either UNCLOSED_STRING or SUCCESS - simdjson_inline error_code finish(); + simdjson_warn_unused simdjson_inline error_code finish(); private: // Whether the last character of the previous iteration is part of a scalar token @@ -31274,7 +31959,7 @@ simdjson_inline json_block json_scanner::next(const simd::simd8x64& in) ); } -simdjson_inline error_code json_scanner::finish() { +simdjson_warn_unused simdjson_inline error_code json_scanner::finish() { return string_scanner.finish(); } @@ -31428,7 +32113,7 @@ class json_minifier { template simdjson_inline void step(const uint8_t *block_buf, buf_block_reader &reader) noexcept; simdjson_inline void next(const simd::simd8x64& in, const json_block& block); - simdjson_inline error_code finish(uint8_t *dst_start, size_t &dst_len); + simdjson_warn_unused simdjson_inline error_code finish(uint8_t *dst_start, size_t &dst_len); json_scanner scanner{}; uint8_t *dst; }; @@ -31438,7 +32123,7 @@ simdjson_inline void json_minifier::next(const simd::simd8x64& in, cons dst += in.compress(mask, dst); } -simdjson_inline error_code json_minifier::finish(uint8_t *dst_start, size_t &dst_len) { +simdjson_warn_unused simdjson_inline error_code json_minifier::finish(uint8_t *dst_start, size_t &dst_len) { error_code error = scanner.finish(); if (error) { dst_len = 0; return error; } dst_len = dst - dst_start; @@ -31647,7 +32332,7 @@ class json_structural_indexer { template simdjson_inline void step(const uint8_t *block, buf_block_reader &reader) noexcept; simdjson_inline void next(const simd::simd8x64& in, const json_block& block, size_t idx); - simdjson_inline error_code finish(dom_parser_implementation &parser, size_t idx, size_t len, stage1_mode partial); + simdjson_warn_unused simdjson_inline error_code finish(dom_parser_implementation &parser, size_t idx, size_t len, stage1_mode partial); json_scanner scanner{}; utf8_checker checker{}; @@ -32499,6 +33184,7 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::visit_primitive(V /* end file generic/stage2/json_iterator.h for ppc64 */ /* including generic/stage2/stringparsing.h for ppc64: #include */ /* begin file generic/stage2/stringparsing.h for ppc64 */ +#include #ifndef SIMDJSON_SRC_GENERIC_STAGE2_STRINGPARSING_H /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ @@ -32651,7 +33337,8 @@ simdjson_inline bool handle_unicode_codepoint_wobbly(const uint8_t **src_ptr, simdjson_warn_unused simdjson_inline uint8_t *parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) { while (1) { // Copy the next n bytes, and find the backslash and quote in them. - auto bs_quote = backslash_and_quote::copy_and_find(src, dst); + auto b = backslash_and_quote{}; + auto bs_quote = b.copy_and_find(src, dst); // If the next thing is the end quote, copy and return if (bs_quote.has_quote_first()) { // we encountered quotes first. Move dst to point to quotes and exit @@ -32696,7 +33383,8 @@ simdjson_warn_unused simdjson_inline uint8_t *parse_wobbly_string(const uint8_t // It is not ideal that this function is nearly identical to parse_string. while (1) { // Copy the next n bytes, and find the backslash and quote in them. - auto bs_quote = backslash_and_quote::copy_and_find(src, dst); + auto b = backslash_and_quote{}; + auto bs_quote = b.copy_and_find(src, dst); // If the next thing is the end quote, copy and return if (bs_quote.has_quote_first()) { // we encountered quotes first. Move dst to point to quotes and exit @@ -32738,6 +33426,7 @@ simdjson_warn_unused simdjson_inline uint8_t *parse_wobbly_string(const uint8_t } } // namespace stringparsing + } // unnamed namespace } // namespace ppc64 } // namespace simdjson @@ -34363,7 +35052,7 @@ using namespace simd; struct backslash_and_quote { public: static constexpr uint32_t BYTES_PROCESSED = 32; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); + simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; } simdjson_inline bool has_backslash() { return bs_bits != 0; } @@ -34389,6 +35078,31 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin }; } + +struct escaping { + static constexpr uint32_t BYTES_PROCESSED = 16; + simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst); + + simdjson_inline bool has_escape() { return escape_bits != 0; } + simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); } + + uint64_t escape_bits; +}; // struct escaping + + + +simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) { + static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes"); + simd8 v(src); + v.store(dst); + simd8 is_quote = (v == '"'); + simd8 is_backslash = (v == '\\'); + simd8 is_control = (v < 32); + return { + uint64_t((is_backslash | is_quote | is_control).to_bitmask()) + }; +} + } // unnamed namespace } // namespace westmere } // namespace simdjson @@ -34806,12 +35520,17 @@ struct implementation_simdjson_result_base { * * @param value The variable to assign the value to. May not be set if there is an error. */ - simdjson_inline error_code get(T &value) && noexcept; + simdjson_warn_unused simdjson_inline error_code get(T &value) && noexcept; /** * The error. */ - simdjson_inline error_code error() const noexcept; + simdjson_warn_unused simdjson_inline error_code error() const noexcept; + + /** + * Whether there is a value. + */ + simdjson_warn_unused simdjson_inline bool has_value() const noexcept; #if SIMDJSON_EXCEPTIONS @@ -34820,6 +35539,16 @@ struct implementation_simdjson_result_base { * * @throw simdjson_error if there was an error. */ + simdjson_inline T& operator*() & noexcept(false); + simdjson_inline T&& operator*() && noexcept(false); + /** + * Arrow operator to access members of the contained value. + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T* operator->() noexcept(false); + simdjson_inline const T* operator->() const noexcept(false); + simdjson_inline T& value() & noexcept(false); /** @@ -34861,6 +35590,7 @@ struct implementation_simdjson_result_base { * the error() method returns a value that evaluates to false. */ simdjson_inline T&& value_unsafe() && noexcept; + protected: /** users should never directly access first and second. **/ T first{}; /** Users should never directly access 'first'. **/ @@ -35237,7 +35967,7 @@ simdjson_inline bool is_digit(const uint8_t c) { return static_cast(c - '0') <= 9; } -simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const uint8_t *const src, const uint8_t *&p, uint64_t &i, int64_t &exponent) { +simdjson_warn_unused simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const uint8_t *const src, const uint8_t *&p, uint64_t &i, int64_t &exponent) { // we continue with the fiction that we have an integer. If the // floating point number is representable as x * 10^z for some integer // z that fits in 53 bits, then we will be able to convert back the @@ -35265,7 +35995,7 @@ simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const u return SUCCESS; } -simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const src, const uint8_t *&p, int64_t &exponent) { +simdjson_warn_unused simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const src, const uint8_t *&p, int64_t &exponent) { // Exp Sign: -123.456e[-]78 bool neg_exp = ('-' == *p); if (neg_exp || '+' == *p) { p++; } // Skip + as well @@ -35354,7 +36084,7 @@ static error_code slow_float_parsing(simdjson_unused const uint8_t * src, double /** @private */ template -simdjson_inline error_code write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer) { +simdjson_warn_unused simdjson_inline error_code write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer) { // If we frequently had to deal with long strings of digits, // we could extend our code by using a 128-bit integer instead // of a 64-bit integer. However, this is uncommon in practice. @@ -35417,13 +36147,13 @@ simdjson_inline error_code write_float(const uint8_t *const src, bool negative, // // Our objective is accurate parsing (ULP of 0) at high speed. template -simdjson_inline error_code parse_number(const uint8_t *const src, W &writer); +simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const src, W &writer); // for performance analysis, it is sometimes useful to skip parsing #ifdef SIMDJSON_SKIPNUMBERPARSING template -simdjson_inline error_code parse_number(const uint8_t *const, W &writer) { +simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const, W &writer) { writer.append_s64(0); // always write zero return SUCCESS; // always succeeds } @@ -35449,7 +36179,7 @@ simdjson_unused simdjson_inline simdjson_result get_number_type(con // // Our objective is accurate parsing (ULP of 0) at high speed. template -simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { +simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { // // Check for minus sign // @@ -36233,12 +36963,41 @@ simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_b } template -simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { +simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { return this->second; } + +template +simdjson_warn_unused simdjson_inline bool implementation_simdjson_result_base::has_value() const noexcept { + return this->error() == SUCCESS; +} + #if SIMDJSON_EXCEPTIONS +template +simdjson_inline T& implementation_simdjson_result_base::operator*() & noexcept(false) { + return this->value(); +} + +template +simdjson_inline T&& implementation_simdjson_result_base::operator*() && noexcept(false) { + return std::forward>(*this).value(); +} + +template +simdjson_inline T* implementation_simdjson_result_base::operator->() noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + + +template +simdjson_inline const T* implementation_simdjson_result_base::operator->() const noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + template simdjson_inline T& implementation_simdjson_result_base::value() & noexcept(false) { if (error()) { throw simdjson_error(error()); } @@ -37442,7 +38201,7 @@ using namespace simd; struct backslash_and_quote { public: static constexpr uint32_t BYTES_PROCESSED = 32; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); + simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; } simdjson_inline bool has_backslash() { return bs_bits != 0; } @@ -37468,6 +38227,31 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin }; } + +struct escaping { + static constexpr uint32_t BYTES_PROCESSED = 16; + simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst); + + simdjson_inline bool has_escape() { return escape_bits != 0; } + simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); } + + uint64_t escape_bits; +}; // struct escaping + + + +simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) { + static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes"); + simd8 v(src); + v.store(dst); + simd8 is_quote = (v == '"'); + simd8 is_backslash = (v == '\\'); + simd8 is_control = (v < 32); + return { + uint64_t((is_backslash | is_quote | is_control).to_bitmask()) + }; +} + } // unnamed namespace } // namespace westmere } // namespace simdjson @@ -38173,7 +38957,7 @@ using namespace simd; } } // do not forget to call check_eof! - simdjson_inline error_code errors() { + simdjson_warn_unused simdjson_inline error_code errors() { return this->error.any_bits_set_anywhere() ? error_code::UTF8_ERROR : error_code::SUCCESS; } @@ -38298,7 +39082,7 @@ class json_scanner { json_scanner() = default; simdjson_inline json_block next(const simd::simd8x64& in); // Returns either UNCLOSED_STRING or SUCCESS - simdjson_inline error_code finish(); + simdjson_warn_unused simdjson_inline error_code finish(); private: // Whether the last character of the previous iteration is part of a scalar token @@ -38346,7 +39130,7 @@ simdjson_inline json_block json_scanner::next(const simd::simd8x64& in) ); } -simdjson_inline error_code json_scanner::finish() { +simdjson_warn_unused simdjson_inline error_code json_scanner::finish() { return string_scanner.finish(); } @@ -38500,7 +39284,7 @@ class json_minifier { template simdjson_inline void step(const uint8_t *block_buf, buf_block_reader &reader) noexcept; simdjson_inline void next(const simd::simd8x64& in, const json_block& block); - simdjson_inline error_code finish(uint8_t *dst_start, size_t &dst_len); + simdjson_warn_unused simdjson_inline error_code finish(uint8_t *dst_start, size_t &dst_len); json_scanner scanner{}; uint8_t *dst; }; @@ -38510,7 +39294,7 @@ simdjson_inline void json_minifier::next(const simd::simd8x64& in, cons dst += in.compress(mask, dst); } -simdjson_inline error_code json_minifier::finish(uint8_t *dst_start, size_t &dst_len) { +simdjson_warn_unused simdjson_inline error_code json_minifier::finish(uint8_t *dst_start, size_t &dst_len) { error_code error = scanner.finish(); if (error) { dst_len = 0; return error; } dst_len = dst - dst_start; @@ -38719,7 +39503,7 @@ class json_structural_indexer { template simdjson_inline void step(const uint8_t *block, buf_block_reader &reader) noexcept; simdjson_inline void next(const simd::simd8x64& in, const json_block& block, size_t idx); - simdjson_inline error_code finish(dom_parser_implementation &parser, size_t idx, size_t len, stage1_mode partial); + simdjson_warn_unused simdjson_inline error_code finish(dom_parser_implementation &parser, size_t idx, size_t len, stage1_mode partial); json_scanner scanner{}; utf8_checker checker{}; @@ -39571,6 +40355,7 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::visit_primitive(V /* end file generic/stage2/json_iterator.h for westmere */ /* including generic/stage2/stringparsing.h for westmere: #include */ /* begin file generic/stage2/stringparsing.h for westmere */ +#include #ifndef SIMDJSON_SRC_GENERIC_STAGE2_STRINGPARSING_H /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ @@ -39723,7 +40508,8 @@ simdjson_inline bool handle_unicode_codepoint_wobbly(const uint8_t **src_ptr, simdjson_warn_unused simdjson_inline uint8_t *parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) { while (1) { // Copy the next n bytes, and find the backslash and quote in them. - auto bs_quote = backslash_and_quote::copy_and_find(src, dst); + auto b = backslash_and_quote{}; + auto bs_quote = b.copy_and_find(src, dst); // If the next thing is the end quote, copy and return if (bs_quote.has_quote_first()) { // we encountered quotes first. Move dst to point to quotes and exit @@ -39768,7 +40554,8 @@ simdjson_warn_unused simdjson_inline uint8_t *parse_wobbly_string(const uint8_t // It is not ideal that this function is nearly identical to parse_string. while (1) { // Copy the next n bytes, and find the backslash and quote in them. - auto bs_quote = backslash_and_quote::copy_and_find(src, dst); + auto b = backslash_and_quote{}; + auto bs_quote = b.copy_and_find(src, dst); // If the next thing is the end quote, copy and return if (bs_quote.has_quote_first()) { // we encountered quotes first. Move dst to point to quotes and exit @@ -39810,6 +40597,7 @@ simdjson_warn_unused simdjson_inline uint8_t *parse_wobbly_string(const uint8_t } } // namespace stringparsing + } // unnamed namespace } // namespace westmere } // namespace simdjson @@ -40338,7 +41126,6 @@ simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t * if (error) { return error; } return stage2(_doc); } - } // namespace westmere } // namespace simdjson @@ -40939,7 +41726,7 @@ using namespace simd; struct backslash_and_quote { public: static constexpr uint32_t BYTES_PROCESSED = 32; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); + simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; } simdjson_inline bool has_backslash() { return bs_bits != 0; } @@ -40968,6 +41755,31 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin }; } + +struct escaping { + static constexpr uint32_t BYTES_PROCESSED = 16; + simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst); + + simdjson_inline bool has_escape() { return escape_bits != 0; } + simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); } + + uint64_t escape_bits; +}; // struct escaping + + + +simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) { + static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes"); + simd8 v(src); + v.store(dst); + simd8 is_quote = (v == '"'); + simd8 is_backslash = (v == '\\'); + simd8 is_control = (v < 32); + return { + static_cast((is_backslash | is_quote | is_control).to_bitmask()) + }; +} + } // unnamed namespace } // namespace lsx } // namespace simdjson @@ -41387,12 +42199,17 @@ struct implementation_simdjson_result_base { * * @param value The variable to assign the value to. May not be set if there is an error. */ - simdjson_inline error_code get(T &value) && noexcept; + simdjson_warn_unused simdjson_inline error_code get(T &value) && noexcept; /** * The error. */ - simdjson_inline error_code error() const noexcept; + simdjson_warn_unused simdjson_inline error_code error() const noexcept; + + /** + * Whether there is a value. + */ + simdjson_warn_unused simdjson_inline bool has_value() const noexcept; #if SIMDJSON_EXCEPTIONS @@ -41401,6 +42218,16 @@ struct implementation_simdjson_result_base { * * @throw simdjson_error if there was an error. */ + simdjson_inline T& operator*() & noexcept(false); + simdjson_inline T&& operator*() && noexcept(false); + /** + * Arrow operator to access members of the contained value. + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T* operator->() noexcept(false); + simdjson_inline const T* operator->() const noexcept(false); + simdjson_inline T& value() & noexcept(false); /** @@ -41442,6 +42269,7 @@ struct implementation_simdjson_result_base { * the error() method returns a value that evaluates to false. */ simdjson_inline T&& value_unsafe() && noexcept; + protected: /** users should never directly access first and second. **/ T first{}; /** Users should never directly access 'first'. **/ @@ -41818,7 +42646,7 @@ simdjson_inline bool is_digit(const uint8_t c) { return static_cast(c - '0') <= 9; } -simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const uint8_t *const src, const uint8_t *&p, uint64_t &i, int64_t &exponent) { +simdjson_warn_unused simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const uint8_t *const src, const uint8_t *&p, uint64_t &i, int64_t &exponent) { // we continue with the fiction that we have an integer. If the // floating point number is representable as x * 10^z for some integer // z that fits in 53 bits, then we will be able to convert back the @@ -41846,7 +42674,7 @@ simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const u return SUCCESS; } -simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const src, const uint8_t *&p, int64_t &exponent) { +simdjson_warn_unused simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const src, const uint8_t *&p, int64_t &exponent) { // Exp Sign: -123.456e[-]78 bool neg_exp = ('-' == *p); if (neg_exp || '+' == *p) { p++; } // Skip + as well @@ -41935,7 +42763,7 @@ static error_code slow_float_parsing(simdjson_unused const uint8_t * src, double /** @private */ template -simdjson_inline error_code write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer) { +simdjson_warn_unused simdjson_inline error_code write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer) { // If we frequently had to deal with long strings of digits, // we could extend our code by using a 128-bit integer instead // of a 64-bit integer. However, this is uncommon in practice. @@ -41998,13 +42826,13 @@ simdjson_inline error_code write_float(const uint8_t *const src, bool negative, // // Our objective is accurate parsing (ULP of 0) at high speed. template -simdjson_inline error_code parse_number(const uint8_t *const src, W &writer); +simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const src, W &writer); // for performance analysis, it is sometimes useful to skip parsing #ifdef SIMDJSON_SKIPNUMBERPARSING template -simdjson_inline error_code parse_number(const uint8_t *const, W &writer) { +simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const, W &writer) { writer.append_s64(0); // always write zero return SUCCESS; // always succeeds } @@ -42030,7 +42858,7 @@ simdjson_unused simdjson_inline simdjson_result get_number_type(con // // Our objective is accurate parsing (ULP of 0) at high speed. template -simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { +simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { // // Check for minus sign // @@ -42814,12 +43642,41 @@ simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_b } template -simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { +simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { return this->second; } + +template +simdjson_warn_unused simdjson_inline bool implementation_simdjson_result_base::has_value() const noexcept { + return this->error() == SUCCESS; +} + #if SIMDJSON_EXCEPTIONS +template +simdjson_inline T& implementation_simdjson_result_base::operator*() & noexcept(false) { + return this->value(); +} + +template +simdjson_inline T&& implementation_simdjson_result_base::operator*() && noexcept(false) { + return std::forward>(*this).value(); +} + +template +simdjson_inline T* implementation_simdjson_result_base::operator->() noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + + +template +simdjson_inline const T* implementation_simdjson_result_base::operator->() const noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + template simdjson_inline T& implementation_simdjson_result_base::value() & noexcept(false) { if (error()) { throw simdjson_error(error()); } @@ -43488,7 +44345,7 @@ using namespace simd; struct backslash_and_quote { public: static constexpr uint32_t BYTES_PROCESSED = 32; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); + simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; } simdjson_inline bool has_backslash() { return bs_bits != 0; } @@ -43517,6 +44374,31 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin }; } + +struct escaping { + static constexpr uint32_t BYTES_PROCESSED = 16; + simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst); + + simdjson_inline bool has_escape() { return escape_bits != 0; } + simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); } + + uint64_t escape_bits; +}; // struct escaping + + + +simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) { + static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes"); + simd8 v(src); + v.store(dst); + simd8 is_quote = (v == '"'); + simd8 is_backslash = (v == '\\'); + simd8 is_control = (v < 32); + return { + static_cast((is_backslash | is_quote | is_control).to_bitmask()) + }; +} + } // unnamed namespace } // namespace lsx } // namespace simdjson @@ -44224,7 +45106,7 @@ using namespace simd; } } // do not forget to call check_eof! - simdjson_inline error_code errors() { + simdjson_warn_unused simdjson_inline error_code errors() { return this->error.any_bits_set_anywhere() ? error_code::UTF8_ERROR : error_code::SUCCESS; } @@ -44349,7 +45231,7 @@ class json_scanner { json_scanner() = default; simdjson_inline json_block next(const simd::simd8x64& in); // Returns either UNCLOSED_STRING or SUCCESS - simdjson_inline error_code finish(); + simdjson_warn_unused simdjson_inline error_code finish(); private: // Whether the last character of the previous iteration is part of a scalar token @@ -44397,7 +45279,7 @@ simdjson_inline json_block json_scanner::next(const simd::simd8x64& in) ); } -simdjson_inline error_code json_scanner::finish() { +simdjson_warn_unused simdjson_inline error_code json_scanner::finish() { return string_scanner.finish(); } @@ -44551,7 +45433,7 @@ class json_minifier { template simdjson_inline void step(const uint8_t *block_buf, buf_block_reader &reader) noexcept; simdjson_inline void next(const simd::simd8x64& in, const json_block& block); - simdjson_inline error_code finish(uint8_t *dst_start, size_t &dst_len); + simdjson_warn_unused simdjson_inline error_code finish(uint8_t *dst_start, size_t &dst_len); json_scanner scanner{}; uint8_t *dst; }; @@ -44561,7 +45443,7 @@ simdjson_inline void json_minifier::next(const simd::simd8x64& in, cons dst += in.compress(mask, dst); } -simdjson_inline error_code json_minifier::finish(uint8_t *dst_start, size_t &dst_len) { +simdjson_warn_unused simdjson_inline error_code json_minifier::finish(uint8_t *dst_start, size_t &dst_len) { error_code error = scanner.finish(); if (error) { dst_len = 0; return error; } dst_len = dst - dst_start; @@ -44770,7 +45652,7 @@ class json_structural_indexer { template simdjson_inline void step(const uint8_t *block, buf_block_reader &reader) noexcept; simdjson_inline void next(const simd::simd8x64& in, const json_block& block, size_t idx); - simdjson_inline error_code finish(dom_parser_implementation &parser, size_t idx, size_t len, stage1_mode partial); + simdjson_warn_unused simdjson_inline error_code finish(dom_parser_implementation &parser, size_t idx, size_t len, stage1_mode partial); json_scanner scanner{}; utf8_checker checker{}; @@ -45622,6 +46504,7 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::visit_primitive(V /* end file generic/stage2/json_iterator.h for lsx */ /* including generic/stage2/stringparsing.h for lsx: #include */ /* begin file generic/stage2/stringparsing.h for lsx */ +#include #ifndef SIMDJSON_SRC_GENERIC_STAGE2_STRINGPARSING_H /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ @@ -45774,7 +46657,8 @@ simdjson_inline bool handle_unicode_codepoint_wobbly(const uint8_t **src_ptr, simdjson_warn_unused simdjson_inline uint8_t *parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) { while (1) { // Copy the next n bytes, and find the backslash and quote in them. - auto bs_quote = backslash_and_quote::copy_and_find(src, dst); + auto b = backslash_and_quote{}; + auto bs_quote = b.copy_and_find(src, dst); // If the next thing is the end quote, copy and return if (bs_quote.has_quote_first()) { // we encountered quotes first. Move dst to point to quotes and exit @@ -45819,7 +46703,8 @@ simdjson_warn_unused simdjson_inline uint8_t *parse_wobbly_string(const uint8_t // It is not ideal that this function is nearly identical to parse_string. while (1) { // Copy the next n bytes, and find the backslash and quote in them. - auto bs_quote = backslash_and_quote::copy_and_find(src, dst); + auto b = backslash_and_quote{}; + auto bs_quote = b.copy_and_find(src, dst); // If the next thing is the end quote, copy and return if (bs_quote.has_quote_first()) { // we encountered quotes first. Move dst to point to quotes and exit @@ -45861,6 +46746,7 @@ simdjson_warn_unused simdjson_inline uint8_t *parse_wobbly_string(const uint8_t } } // namespace stringparsing + } // unnamed namespace } // namespace lsx } // namespace simdjson @@ -46971,7 +47857,7 @@ using namespace simd; struct backslash_and_quote { public: static constexpr uint32_t BYTES_PROCESSED = 32; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); + simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; } simdjson_inline bool has_backslash() { return bs_bits != 0; } @@ -46994,6 +47880,31 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin }; } + +struct escaping { + static constexpr uint32_t BYTES_PROCESSED = 16; + simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst); + + simdjson_inline bool has_escape() { return escape_bits != 0; } + simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); } + + uint64_t escape_bits; +}; // struct escaping + + + +simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) { + static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes"); + simd8 v(src); + v.store(dst); + simd8 is_quote = (v == '"'); + simd8 is_backslash = (v == '\\'); + simd8 is_control = (v < 32); + return { + (is_backslash | is_quote | is_control).to_bitmask() + }; +} + } // unnamed namespace } // namespace lasx } // namespace simdjson @@ -47413,12 +48324,17 @@ struct implementation_simdjson_result_base { * * @param value The variable to assign the value to. May not be set if there is an error. */ - simdjson_inline error_code get(T &value) && noexcept; + simdjson_warn_unused simdjson_inline error_code get(T &value) && noexcept; /** * The error. */ - simdjson_inline error_code error() const noexcept; + simdjson_warn_unused simdjson_inline error_code error() const noexcept; + + /** + * Whether there is a value. + */ + simdjson_warn_unused simdjson_inline bool has_value() const noexcept; #if SIMDJSON_EXCEPTIONS @@ -47427,6 +48343,16 @@ struct implementation_simdjson_result_base { * * @throw simdjson_error if there was an error. */ + simdjson_inline T& operator*() & noexcept(false); + simdjson_inline T&& operator*() && noexcept(false); + /** + * Arrow operator to access members of the contained value. + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T* operator->() noexcept(false); + simdjson_inline const T* operator->() const noexcept(false); + simdjson_inline T& value() & noexcept(false); /** @@ -47468,6 +48394,7 @@ struct implementation_simdjson_result_base { * the error() method returns a value that evaluates to false. */ simdjson_inline T&& value_unsafe() && noexcept; + protected: /** users should never directly access first and second. **/ T first{}; /** Users should never directly access 'first'. **/ @@ -47844,7 +48771,7 @@ simdjson_inline bool is_digit(const uint8_t c) { return static_cast(c - '0') <= 9; } -simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const uint8_t *const src, const uint8_t *&p, uint64_t &i, int64_t &exponent) { +simdjson_warn_unused simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const uint8_t *const src, const uint8_t *&p, uint64_t &i, int64_t &exponent) { // we continue with the fiction that we have an integer. If the // floating point number is representable as x * 10^z for some integer // z that fits in 53 bits, then we will be able to convert back the @@ -47872,7 +48799,7 @@ simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const u return SUCCESS; } -simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const src, const uint8_t *&p, int64_t &exponent) { +simdjson_warn_unused simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const src, const uint8_t *&p, int64_t &exponent) { // Exp Sign: -123.456e[-]78 bool neg_exp = ('-' == *p); if (neg_exp || '+' == *p) { p++; } // Skip + as well @@ -47961,7 +48888,7 @@ static error_code slow_float_parsing(simdjson_unused const uint8_t * src, double /** @private */ template -simdjson_inline error_code write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer) { +simdjson_warn_unused simdjson_inline error_code write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer) { // If we frequently had to deal with long strings of digits, // we could extend our code by using a 128-bit integer instead // of a 64-bit integer. However, this is uncommon in practice. @@ -48024,13 +48951,13 @@ simdjson_inline error_code write_float(const uint8_t *const src, bool negative, // // Our objective is accurate parsing (ULP of 0) at high speed. template -simdjson_inline error_code parse_number(const uint8_t *const src, W &writer); +simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const src, W &writer); // for performance analysis, it is sometimes useful to skip parsing #ifdef SIMDJSON_SKIPNUMBERPARSING template -simdjson_inline error_code parse_number(const uint8_t *const, W &writer) { +simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const, W &writer) { writer.append_s64(0); // always write zero return SUCCESS; // always succeeds } @@ -48056,7 +48983,7 @@ simdjson_unused simdjson_inline simdjson_result get_number_type(con // // Our objective is accurate parsing (ULP of 0) at high speed. template -simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { +simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { // // Check for minus sign // @@ -48840,12 +49767,41 @@ simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_b } template -simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { +simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { return this->second; } + +template +simdjson_warn_unused simdjson_inline bool implementation_simdjson_result_base::has_value() const noexcept { + return this->error() == SUCCESS; +} + #if SIMDJSON_EXCEPTIONS +template +simdjson_inline T& implementation_simdjson_result_base::operator*() & noexcept(false) { + return this->value(); +} + +template +simdjson_inline T&& implementation_simdjson_result_base::operator*() && noexcept(false) { + return std::forward>(*this).value(); +} + +template +simdjson_inline T* implementation_simdjson_result_base::operator->() noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + + +template +simdjson_inline const T* implementation_simdjson_result_base::operator->() const noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + template simdjson_inline T& implementation_simdjson_result_base::value() & noexcept(false) { if (error()) { throw simdjson_error(error()); } @@ -49536,7 +50492,7 @@ using namespace simd; struct backslash_and_quote { public: static constexpr uint32_t BYTES_PROCESSED = 32; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); + simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; } simdjson_inline bool has_backslash() { return bs_bits != 0; } @@ -49559,6 +50515,31 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin }; } + +struct escaping { + static constexpr uint32_t BYTES_PROCESSED = 16; + simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst); + + simdjson_inline bool has_escape() { return escape_bits != 0; } + simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); } + + uint64_t escape_bits; +}; // struct escaping + + + +simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) { + static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes"); + simd8 v(src); + v.store(dst); + simd8 is_quote = (v == '"'); + simd8 is_backslash = (v == '\\'); + simd8 is_control = (v < 32); + return { + (is_backslash | is_quote | is_control).to_bitmask() + }; +} + } // unnamed namespace } // namespace lasx } // namespace simdjson @@ -50266,7 +51247,7 @@ using namespace simd; } } // do not forget to call check_eof! - simdjson_inline error_code errors() { + simdjson_warn_unused simdjson_inline error_code errors() { return this->error.any_bits_set_anywhere() ? error_code::UTF8_ERROR : error_code::SUCCESS; } @@ -50391,7 +51372,7 @@ class json_scanner { json_scanner() = default; simdjson_inline json_block next(const simd::simd8x64& in); // Returns either UNCLOSED_STRING or SUCCESS - simdjson_inline error_code finish(); + simdjson_warn_unused simdjson_inline error_code finish(); private: // Whether the last character of the previous iteration is part of a scalar token @@ -50439,7 +51420,7 @@ simdjson_inline json_block json_scanner::next(const simd::simd8x64& in) ); } -simdjson_inline error_code json_scanner::finish() { +simdjson_warn_unused simdjson_inline error_code json_scanner::finish() { return string_scanner.finish(); } @@ -50593,7 +51574,7 @@ class json_minifier { template simdjson_inline void step(const uint8_t *block_buf, buf_block_reader &reader) noexcept; simdjson_inline void next(const simd::simd8x64& in, const json_block& block); - simdjson_inline error_code finish(uint8_t *dst_start, size_t &dst_len); + simdjson_warn_unused simdjson_inline error_code finish(uint8_t *dst_start, size_t &dst_len); json_scanner scanner{}; uint8_t *dst; }; @@ -50603,7 +51584,7 @@ simdjson_inline void json_minifier::next(const simd::simd8x64& in, cons dst += in.compress(mask, dst); } -simdjson_inline error_code json_minifier::finish(uint8_t *dst_start, size_t &dst_len) { +simdjson_warn_unused simdjson_inline error_code json_minifier::finish(uint8_t *dst_start, size_t &dst_len) { error_code error = scanner.finish(); if (error) { dst_len = 0; return error; } dst_len = dst - dst_start; @@ -50812,7 +51793,7 @@ class json_structural_indexer { template simdjson_inline void step(const uint8_t *block, buf_block_reader &reader) noexcept; simdjson_inline void next(const simd::simd8x64& in, const json_block& block, size_t idx); - simdjson_inline error_code finish(dom_parser_implementation &parser, size_t idx, size_t len, stage1_mode partial); + simdjson_warn_unused simdjson_inline error_code finish(dom_parser_implementation &parser, size_t idx, size_t len, stage1_mode partial); json_scanner scanner{}; utf8_checker checker{}; @@ -51664,6 +52645,7 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::visit_primitive(V /* end file generic/stage2/json_iterator.h for lasx */ /* including generic/stage2/stringparsing.h for lasx: #include */ /* begin file generic/stage2/stringparsing.h for lasx */ +#include #ifndef SIMDJSON_SRC_GENERIC_STAGE2_STRINGPARSING_H /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ @@ -51816,7 +52798,8 @@ simdjson_inline bool handle_unicode_codepoint_wobbly(const uint8_t **src_ptr, simdjson_warn_unused simdjson_inline uint8_t *parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) { while (1) { // Copy the next n bytes, and find the backslash and quote in them. - auto bs_quote = backslash_and_quote::copy_and_find(src, dst); + auto b = backslash_and_quote{}; + auto bs_quote = b.copy_and_find(src, dst); // If the next thing is the end quote, copy and return if (bs_quote.has_quote_first()) { // we encountered quotes first. Move dst to point to quotes and exit @@ -51861,7 +52844,8 @@ simdjson_warn_unused simdjson_inline uint8_t *parse_wobbly_string(const uint8_t // It is not ideal that this function is nearly identical to parse_string. while (1) { // Copy the next n bytes, and find the backslash and quote in them. - auto bs_quote = backslash_and_quote::copy_and_find(src, dst); + auto b = backslash_and_quote{}; + auto bs_quote = b.copy_and_find(src, dst); // If the next thing is the end quote, copy and return if (bs_quote.has_quote_first()) { // we encountered quotes first. Move dst to point to quotes and exit @@ -51903,6 +52887,7 @@ simdjson_warn_unused simdjson_inline uint8_t *parse_wobbly_string(const uint8_t } } // namespace stringparsing + } // unnamed namespace } // namespace lasx } // namespace simdjson @@ -52516,7 +53501,7 @@ namespace { struct backslash_and_quote { public: static constexpr uint32_t BYTES_PROCESSED = 1; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); + simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); simdjson_inline bool has_quote_first() { return c == '"'; } simdjson_inline bool has_backslash() { return c == '\\'; } @@ -52532,6 +53517,24 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin return { src[0] }; } + +struct escaping { + static constexpr uint32_t BYTES_PROCESSED = 1; + simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst); + + simdjson_inline bool has_escape() { return escape_bits; } + simdjson_inline int escape_index() { return 0; } + + bool escape_bits; +}; // struct escaping + + + +simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) { + dst[0] = src[0]; + return { (src[0] == '\\') || (src[0] == '"') || (src[0] < 32) }; +} + } // unnamed namespace } // namespace fallback } // namespace simdjson @@ -53038,12 +54041,17 @@ struct implementation_simdjson_result_base { * * @param value The variable to assign the value to. May not be set if there is an error. */ - simdjson_inline error_code get(T &value) && noexcept; + simdjson_warn_unused simdjson_inline error_code get(T &value) && noexcept; /** * The error. */ - simdjson_inline error_code error() const noexcept; + simdjson_warn_unused simdjson_inline error_code error() const noexcept; + + /** + * Whether there is a value. + */ + simdjson_warn_unused simdjson_inline bool has_value() const noexcept; #if SIMDJSON_EXCEPTIONS @@ -53052,6 +54060,16 @@ struct implementation_simdjson_result_base { * * @throw simdjson_error if there was an error. */ + simdjson_inline T& operator*() & noexcept(false); + simdjson_inline T&& operator*() && noexcept(false); + /** + * Arrow operator to access members of the contained value. + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T* operator->() noexcept(false); + simdjson_inline const T* operator->() const noexcept(false); + simdjson_inline T& value() & noexcept(false); /** @@ -53093,6 +54111,7 @@ struct implementation_simdjson_result_base { * the error() method returns a value that evaluates to false. */ simdjson_inline T&& value_unsafe() && noexcept; + protected: /** users should never directly access first and second. **/ T first{}; /** Users should never directly access 'first'. **/ @@ -53469,7 +54488,7 @@ simdjson_inline bool is_digit(const uint8_t c) { return static_cast(c - '0') <= 9; } -simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const uint8_t *const src, const uint8_t *&p, uint64_t &i, int64_t &exponent) { +simdjson_warn_unused simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const uint8_t *const src, const uint8_t *&p, uint64_t &i, int64_t &exponent) { // we continue with the fiction that we have an integer. If the // floating point number is representable as x * 10^z for some integer // z that fits in 53 bits, then we will be able to convert back the @@ -53497,7 +54516,7 @@ simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const u return SUCCESS; } -simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const src, const uint8_t *&p, int64_t &exponent) { +simdjson_warn_unused simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const src, const uint8_t *&p, int64_t &exponent) { // Exp Sign: -123.456e[-]78 bool neg_exp = ('-' == *p); if (neg_exp || '+' == *p) { p++; } // Skip + as well @@ -53586,7 +54605,7 @@ static error_code slow_float_parsing(simdjson_unused const uint8_t * src, double /** @private */ template -simdjson_inline error_code write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer) { +simdjson_warn_unused simdjson_inline error_code write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer) { // If we frequently had to deal with long strings of digits, // we could extend our code by using a 128-bit integer instead // of a 64-bit integer. However, this is uncommon in practice. @@ -53649,13 +54668,13 @@ simdjson_inline error_code write_float(const uint8_t *const src, bool negative, // // Our objective is accurate parsing (ULP of 0) at high speed. template -simdjson_inline error_code parse_number(const uint8_t *const src, W &writer); +simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const src, W &writer); // for performance analysis, it is sometimes useful to skip parsing #ifdef SIMDJSON_SKIPNUMBERPARSING template -simdjson_inline error_code parse_number(const uint8_t *const, W &writer) { +simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const, W &writer) { writer.append_s64(0); // always write zero return SUCCESS; // always succeeds } @@ -53681,7 +54700,7 @@ simdjson_unused simdjson_inline simdjson_result get_number_type(con // // Our objective is accurate parsing (ULP of 0) at high speed. template -simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { +simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { // // Check for minus sign // @@ -54465,12 +55484,41 @@ simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_b } template -simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { +simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { return this->second; } + +template +simdjson_warn_unused simdjson_inline bool implementation_simdjson_result_base::has_value() const noexcept { + return this->error() == SUCCESS; +} + #if SIMDJSON_EXCEPTIONS +template +simdjson_inline T& implementation_simdjson_result_base::operator*() & noexcept(false) { + return this->value(); +} + +template +simdjson_inline T&& implementation_simdjson_result_base::operator*() && noexcept(false) { + return std::forward>(*this).value(); +} + +template +simdjson_inline T* implementation_simdjson_result_base::operator->() noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + + +template +simdjson_inline const T* implementation_simdjson_result_base::operator->() const noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + template simdjson_inline T& implementation_simdjson_result_base::value() & noexcept(false) { if (error()) { throw simdjson_error(error()); } @@ -54670,7 +55718,7 @@ namespace { struct backslash_and_quote { public: static constexpr uint32_t BYTES_PROCESSED = 1; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); + simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); simdjson_inline bool has_quote_first() { return c == '"'; } simdjson_inline bool has_backslash() { return c == '\\'; } @@ -54686,6 +55734,24 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin return { src[0] }; } + +struct escaping { + static constexpr uint32_t BYTES_PROCESSED = 1; + simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst); + + simdjson_inline bool has_escape() { return escape_bits; } + simdjson_inline int escape_index() { return 0; } + + bool escape_bits; +}; // struct escaping + + + +simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) { + dst[0] = src[0]; + return { (src[0] == '\\') || (src[0] == '"') || (src[0] < 32) }; +} + } // unnamed namespace } // namespace fallback } // namespace simdjson @@ -54892,6 +55958,7 @@ simdjson_inline uint32_t find_next_document_index(dom_parser_implementation &par /* end file generic/stage1/find_next_document_index.h for fallback */ /* including generic/stage2/stringparsing.h for fallback: #include */ /* begin file generic/stage2/stringparsing.h for fallback */ +#include #ifndef SIMDJSON_SRC_GENERIC_STAGE2_STRINGPARSING_H /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ @@ -55044,7 +56111,8 @@ simdjson_inline bool handle_unicode_codepoint_wobbly(const uint8_t **src_ptr, simdjson_warn_unused simdjson_inline uint8_t *parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) { while (1) { // Copy the next n bytes, and find the backslash and quote in them. - auto bs_quote = backslash_and_quote::copy_and_find(src, dst); + auto b = backslash_and_quote{}; + auto bs_quote = b.copy_and_find(src, dst); // If the next thing is the end quote, copy and return if (bs_quote.has_quote_first()) { // we encountered quotes first. Move dst to point to quotes and exit @@ -55089,7 +56157,8 @@ simdjson_warn_unused simdjson_inline uint8_t *parse_wobbly_string(const uint8_t // It is not ideal that this function is nearly identical to parse_string. while (1) { // Copy the next n bytes, and find the backslash and quote in them. - auto bs_quote = backslash_and_quote::copy_and_find(src, dst); + auto b = backslash_and_quote{}; + auto bs_quote = b.copy_and_find(src, dst); // If the next thing is the end quote, copy and return if (bs_quote.has_quote_first()) { // we encountered quotes first. Move dst to point to quotes and exit @@ -55131,6 +56200,7 @@ simdjson_warn_unused simdjson_inline uint8_t *parse_wobbly_string(const uint8_t } } // namespace stringparsing + } // unnamed namespace } // namespace fallback } // namespace simdjson @@ -56097,10 +57167,78 @@ simdjson_inline void validate_utf8_character() { idx += 4; } +static const uint8_t CHAR_TYPE_SPACE = 1 << 0; +static const uint8_t CHAR_TYPE_OPERATOR = 1 << 1; +static const uint8_t CHAR_TYPE_ESC_ASCII = 1 << 2; +static const uint8_t CHAR_TYPE_NON_ASCII = 1 << 3; + +const uint8_t char_table[256] = { + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x05, 0x05, 0x04, 0x04, 0x05, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x04, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08 +}; + +simdjson_inline bool char_is_type(uint8_t c, uint8_t type) { + return (char_table[c] & type); +} + +simdjson_inline bool char_is_space(uint8_t c) { + return char_is_type(c, CHAR_TYPE_SPACE); +} + +simdjson_inline bool char_is_operator(uint8_t c) { + return char_is_type(c, CHAR_TYPE_OPERATOR); +} + +simdjson_inline bool char_is_space_or_operator(uint8_t c) { + return char_is_type(c, CHAR_TYPE_SPACE | CHAR_TYPE_OPERATOR); +} + +simdjson_inline bool char_is_ascii_stop(uint8_t c) { + return char_is_type(c, CHAR_TYPE_ESC_ASCII | CHAR_TYPE_NON_ASCII); +} + // Returns true if the string is unclosed. simdjson_inline bool validate_string() { idx++; // skip first quote - while (idx < len && buf[idx] != '"') { + while (idx < len) { + do { + if (char_is_ascii_stop(buf[idx])) { break; } + idx++; + } while (idx < len); + if (idx >= len) { return true; } + if (buf[idx] == '"') { + return false; + } if (buf[idx] == '\\') { idx += 2; } else if (simdjson_unlikely(buf[idx] & 0x80)) { @@ -56114,43 +57252,31 @@ simdjson_inline bool validate_string() { return false; } -simdjson_inline bool is_whitespace_or_operator(uint8_t c) { - switch (c) { - case '{': case '}': case '[': case ']': case ',': case ':': - case ' ': case '\r': case '\n': case '\t': - return true; - default: - return false; - } -} - // // Parse the entire input in STEP_SIZE-byte chunks. // -simdjson_inline error_code scan() { +simdjson_warn_unused simdjson_inline error_code scan() { bool unclosed_string = false; for (;idx= len) { break; } + // String + if (buf[idx] == '"') { + add_structural(); + unclosed_string |= validate_string(); + // Operator + } else if (char_is_operator(buf[idx])) { + add_structural(); + // Primitive or invalid character (invalid characters will be checked in stage 2) + } else { + // Anything else, add the structural and go until we find the next one + add_structural(); + while (idx+1) #include #endif #endif +// The current specification is unclear on how we detect +// static reflection, both __cpp_lib_reflection and +// __cpp_impl_reflection are proposed in the draft specification. +// For now, we disable static reflect by default. It must be +// specified at compiler time. +#ifndef SIMDJSON_STATIC_REFLECTION +#define SIMDJSON_STATIC_REFLECTION 0 // disabled by default. +#endif + #if defined(__apple_build_version__) #if __apple_build_version__ < 14000000 #define SIMDJSON_CONCEPT_DISABLED 1 // apple-clang/13 doesn't support std::convertible_to #endif #endif +#if defined(__cpp_lib_ranges) && __cpp_lib_ranges >= 201911L +#include +#define SIMDJSON_SUPPORTS_RANGES 1 +#else +#define SIMDJSON_SUPPORTS_RANGES 0 +#endif #if defined(__cpp_concepts) && !defined(SIMDJSON_CONCEPT_DISABLED) #if __cpp_concepts >= 201907L #include -#define SIMDJSON_SUPPORTS_DESERIALIZATION 1 +#define SIMDJSON_SUPPORTS_CONCEPTS 1 #else -#define SIMDJSON_SUPPORTS_DESERIALIZATION 0 +#define SIMDJSON_SUPPORTS_CONCEPTS 0 #endif #else // defined(__cpp_concepts) && !defined(SIMDJSON_CONCEPT_DISABLED) -#define SIMDJSON_SUPPORTS_DESERIALIZATION 0 +#define SIMDJSON_SUPPORTS_CONCEPTS 0 #endif // defined(__cpp_concepts) && !defined(SIMDJSON_CONCEPT_DISABLED) +// copy SIMDJSON_SUPPORTS_CONCEPTS to SIMDJSON_SUPPORTS_DESERIALIZATION. +#if SIMDJSON_SUPPORTS_CONCEPTS +#define SIMDJSON_SUPPORTS_DESERIALIZATION 1 +#else +#define SIMDJSON_SUPPORTS_DESERIALIZATION 0 +#endif + + +#if !defined(SIMDJSON_CONSTEVAL) +#if defined(__cpp_consteval) && __cpp_consteval >= 201811L && defined(__cpp_lib_constexpr_string) && __cpp_lib_constexpr_string >= 201907L +#define SIMDJSON_CONSTEVAL 1 +#else +#define SIMDJSON_CONSTEVAL 0 +#endif // defined(__cpp_consteval) && __cpp_consteval >= 201811L && defined(__cpp_lib_constexpr_string) && __cpp_lib_constexpr_string >= 201907L +#endif // !defined(SIMDJSON_CONSTEVAL) + #endif // SIMDJSON_COMPILER_CHECK_H /* end file simdjson/compiler_check.h */ /* including simdjson/portability.h: #include "simdjson/portability.h" */ @@ -172,6 +213,22 @@ using std::size_t; #define SIMDJSON_IS_ARM64 1 #elif defined(__riscv) && __riscv_xlen == 64 #define SIMDJSON_IS_RISCV64 1 + #if __riscv_v_intrinsic >= 11000 + #define SIMDJSON_HAS_RVV_INTRINSICS 1 + #endif + + #define SIMDJSON_HAS_ZVBB_INTRINSICS \ + 0 // there is currently no way to detect this + + #if SIMDJSON_HAS_RVV_INTRINSICS && __riscv_vector && \ + __riscv_v_min_vlen >= 128 && __riscv_v_elen >= 64 + // RISC-V V extension + #define SIMDJSON_IS_RVV 1 + #if SIMDJSON_HAS_ZVBB_INTRINSICS && __riscv_zvbb >= 1000000 + // RISC-V Vector Basic Bit-manipulation + #define SIMDJSON_IS_ZVBB 1 + #endif + #endif #elif defined(__loongarch_lp64) #define SIMDJSON_IS_LOONGARCH64 1 #elif defined(__PPC64__) || defined(_M_PPC64) @@ -318,6 +375,7 @@ using std::size_t; #if defined(NDEBUG) || defined(__OPTIMIZE__) || (defined(_MSC_VER) && !defined(_DEBUG)) // If NDEBUG is set, or __OPTIMIZE__ is set, or we are under MSVC in release mode, // then do away with asserts and use __assume. +// We still recommend that our users set NDEBUG in release mode. #if SIMDJSON_VISUAL_STUDIO #define SIMDJSON_UNREACHABLE() __assume(0) #define SIMDJSON_ASSUME(COND) __assume(COND) @@ -569,17 +627,6 @@ double from_chars(const char *first, const char* end) noexcept; // We assume by default static linkage #define SIMDJSON_DLLIMPORTEXPORT #endif - -/** - * Workaround for the vcpkg package manager. Only vcpkg should - * ever touch the next line. The SIMDJSON_USING_LIBRARY macro is otherwise unused. - */ -#if SIMDJSON_USING_LIBRARY -#define SIMDJSON_DLLIMPORTEXPORT __declspec(dllimport) -#endif -/** - * End of workaround for the vcpkg package manager. - */ #else #define SIMDJSON_DLLIMPORTEXPORT #endif @@ -1042,9 +1089,9 @@ using std::operator<<; #endif #if nssv_HAVE_NODISCARD -# define nssv_nodiscard [[nodiscard]] +# define nssv_nodiscard simdjson_warn_unused #else -# define nssv_nodiscard /*[[nodiscard]]*/ +# define nssv_nodiscard /*simdjson_warn_unused*/ #endif // Additional includes: @@ -2362,16 +2409,25 @@ namespace std { // It could also wrongly set SIMDJSON_DEVELOPMENT_CHECKS (e.g., if the programmer // sets _DEBUG in a release build under Visual Studio, or if some compiler fails to // set the __OPTIMIZE__ macro). +// We make it so that if NDEBUG is defined, then SIMDJSON_DEVELOPMENT_CHECKS +// is not defined, irrespective of the compiler. +// We recommend that users set NDEBUG in release builds, so that +// SIMDJSON_DEVELOPMENT_CHECKS is not defined in release builds by default, +// irrespective of the compiler. #ifndef SIMDJSON_DEVELOPMENT_CHECKS #ifdef _MSC_VER // Visual Studio seems to set _DEBUG for debug builds. -#ifdef _DEBUG +// We set SIMDJSON_DEVELOPMENT_CHECKS to 1 if _DEBUG is defined +// and NDEBUG is not defined. +#if defined(_DEBUG) && !defined(NDEBUG) #define SIMDJSON_DEVELOPMENT_CHECKS 1 #endif // _DEBUG #else // _MSC_VER // All other compilers appear to set __OPTIMIZE__ to a positive integer // when the compiler is optimizing. -#ifndef __OPTIMIZE__ +// We only set SIMDJSON_DEVELOPMENT_CHECKS if both __OPTIMIZE__ +// and NDEBUG are not defined. +#if !defined(__OPTIMIZE__) && !defined(NDEBUG) #define SIMDJSON_DEVELOPMENT_CHECKS 1 #endif // __OPTIMIZE__ #endif // _MSC_VER @@ -2427,6 +2483,18 @@ namespace std { #define SIMDJSON_AVX512_ALLOWED 1 #endif + +#ifndef __has_cpp_attribute +#define simdjson_lifetime_bound +#elif __has_cpp_attribute(msvc::lifetimebound) +#define simdjson_lifetime_bound [[msvc::lifetimebound]] +#elif __has_cpp_attribute(clang::lifetimebound) +#define simdjson_lifetime_bound [[clang::lifetimebound]] +#elif __has_cpp_attribute(lifetimebound) +#define simdjson_lifetime_bound [[lifetimebound]] +#else +#define simdjson_lifetime_bound +#endif #endif // SIMDJSON_COMMON_DEFS_H /* end file simdjson/common_defs.h */ @@ -2440,22 +2508,22 @@ namespace std { #define SIMDJSON_SIMDJSON_VERSION_H /** The version of simdjson being used (major.minor.revision) */ -#define SIMDJSON_VERSION "3.13.0" +#define SIMDJSON_VERSION "4.0.7" namespace simdjson { enum { /** * The major version (MAJOR.minor.revision) of simdjson being used. */ - SIMDJSON_VERSION_MAJOR = 3, + SIMDJSON_VERSION_MAJOR = 4, /** * The minor version (major.MINOR.revision) of simdjson being used. */ - SIMDJSON_VERSION_MINOR = 13, + SIMDJSON_VERSION_MINOR = 0, /** * The revision (major.minor.REVISION) of simdjson being used. */ - SIMDJSON_VERSION_REVISION = 0 + SIMDJSON_VERSION_REVISION = 7 }; } // namespace simdjson @@ -2526,7 +2594,8 @@ enum error_code { SCALAR_DOCUMENT_AS_VALUE, ///< A scalar document is treated as a value. OUT_OF_BOUNDS, ///< Attempted to access location outside of document. TRAILING_CONTENT, ///< Unexpected trailing content in the JSON input - NUM_ERROR_CODES + OUT_OF_CAPACITY, ///< The capacity was exceeded, we cannot allocate enough memory. + NUM_ERROR_CODES ///< Placeholder for end of error code list. }; /** @@ -2584,6 +2653,10 @@ namespace internal { /** * The result of a simdjson operation that could fail. * + * IMPORTANT: For the ondemand API, we use implementation_simdjson_result_base as a base class + * to avoid some compilation issue. Thus, if you modify this class, please ensure that the ondemand + * implementation_simdjson_result_base is also modified. + * * Gives the option of reading error codes, or throwing an exception by casting to the desired result. * * This is a base class for implementations that want to add functions to the result type for @@ -2644,8 +2717,27 @@ struct simdjson_result_base : protected std::pair { */ simdjson_inline error_code error() const noexcept; + /** + * Whether there is a value. + */ + simdjson_inline bool has_value() const noexcept; #if SIMDJSON_EXCEPTIONS + /** + * Dereference operator to access the contained value. + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T& operator*() & noexcept(false); + simdjson_inline T&& operator*() && noexcept(false); + /** + * Arrow operator to access members of the contained value. + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T* operator->() noexcept(false); + simdjson_inline const T* operator->() const noexcept(false); + /** * Get the result value. * @@ -2679,12 +2771,42 @@ struct simdjson_result_base : protected std::pair { /** * Get the result value. This function is safe if and only * the error() method returns a value that evaluates to false. + * We discourage the use of value_unsafe(). + * + * The recommended pattern is: + * + * T value; // where T is the type + * auto error = result.get(value); + * if (error) { + * // handle error + * } + * + * Or you may call 'value()' which will raise an exception + * in case of error: + * + * T value = result.value(); */ simdjson_inline const T& value_unsafe() const& noexcept; /** * Take the result value (move it). This function is safe if and only * the error() method returns a value that evaluates to false. + * We discourage the use of value_unsafe(). + * + * The recommended pattern is: + * + * T value; // where T is the type + * auto error = result.get(value); + * if (error) { + * // handle error, return, exit, abort + * } else { + * // use value here. + * } + * + * Or you may call 'value()' which will raise an exception + * in case of error: + * + * T value = result.value(); */ simdjson_inline T&& value_unsafe() && noexcept; @@ -2699,6 +2821,7 @@ struct simdjson_result_base : protected std::pair { */ template struct simdjson_result : public internal::simdjson_result_base { + /** * @private Create a new empty result with error = UNINITIALIZED. */ @@ -2730,24 +2853,33 @@ struct simdjson_result : public internal::simdjson_result_base { * @param value The variable to assign the value to. May not be set if there is an error. */ simdjson_warn_unused simdjson_inline error_code get(T &value) && noexcept; -// + /** * Copy the value to a provided std::string, only enabled for std::string_view. * * @param value The variable to assign the value to. May not be set if there is an error. */ - simdjson_warn_unused simdjson_inline error_code get(std::string &value) && noexcept -#if SIMDJSON_SUPPORTS_DESERIALIZATION - requires (!std::is_same_v) -#endif // SIMDJSON_SUPPORTS_DESERIALIZATION - ; + template + simdjson_warn_unused simdjson_inline error_code get(std::string &value) && noexcept { + static_assert(std::is_same::value, "SFINAE"); + std::string_view v; + error_code error = std::forward>(*this).get(v); + if (!error) { + value.assign(v.data(), v.size()); + } + return error; + } + /** * The error. */ simdjson_inline error_code error() const noexcept; -#if SIMDJSON_EXCEPTIONS + +#if SIMDJSON_EXCEPTIONS + using internal::simdjson_result_base::operator*; + using internal::simdjson_result_base::operator->; /** * Get the result value. * @@ -2818,7 +2950,7 @@ inline const std::string error_message(int error) noexcept; /* begin file simdjson/concepts.h */ #ifndef SIMDJSON_CONCEPTS_H #define SIMDJSON_CONCEPTS_H -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS #include #include @@ -2850,7 +2982,9 @@ SIMDJSON_IMPL_CONCEPT(op_append, operator+=) #undef SIMDJSON_IMPL_CONCEPT } // namespace details - +template +concept is_pair = requires { typename T::first_type; typename T::second_type; } && + std::same_as>; template concept string_view_like = std::is_convertible_v && !std::is_convertible_v; @@ -2933,21 +3067,133 @@ concept optional_type = requires(std::remove_cvref_t obj) { { obj.value() } -> std::same_as::value_type&>; requires requires(typename std::remove_cvref_t::value_type &&val) { obj.emplace(std::move(val)); - obj = std::move(val); { obj.value_or(val) } -> std::convertible_to::value_type>; }; { static_cast(obj) } -> std::same_as; // convertible to bool + { obj.reset() } noexcept -> std::same_as; }; +// Types we serialize as JSON strings (not as containers) +template +concept string_like = + std::is_same_v, std::string> || + std::is_same_v, std::string_view> || + std::is_same_v, const char*> || + std::is_same_v, char*>; + +// Concept that checks if a type is a container but not a string (because +// strings handling must be handled differently) +// Now uses iterator-based approach for broader container support +template +concept container_but_not_string = + std::ranges::input_range && !string_like && !concepts::string_view_keyed_map; + + } // namespace concepts + + +/** + * We use tag_invoke as our customization point mechanism. + */ +template +concept tag_invocable = requires(Tag tag, Args... args) { + tag_invoke(std::forward(tag), std::forward(args)...); +}; + +template +concept nothrow_tag_invocable = + tag_invocable && requires(Tag tag, Args... args) { + { + tag_invoke(std::forward(tag), std::forward(args)...) + } noexcept; + }; + } // namespace simdjson -#endif // SIMDJSON_SUPPORTS_DESERIALIZATION +#endif // SIMDJSON_SUPPORTS_CONCEPTS #endif // SIMDJSON_CONCEPTS_H /* end file simdjson/concepts.h */ +/* including simdjson/constevalutil.h: #include "simdjson/constevalutil.h" */ +/* begin file simdjson/constevalutil.h */ +#ifndef SIMDJSON_CONSTEVALUTIL_H +#define SIMDJSON_CONSTEVALUTIL_H + +#include +#include +#include + +namespace simdjson { +namespace constevalutil { +#if SIMDJSON_CONSTEVAL + +constexpr static std::array json_quotable_character = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 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, 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, 1, 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, 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, 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, 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, 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, 0, 0, 0}; +constexpr static std::array control_chars = { + "\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", "\\u0006", + "\\u0007", "\\b", "\\t", "\\n", "\\u000b", "\\f", "\\r", + "\\u000e", "\\u000f", "\\u0010", "\\u0011", "\\u0012", "\\u0013", "\\u0014", + "\\u0015", "\\u0016", "\\u0017", "\\u0018", "\\u0019", "\\u001a", "\\u001b", + "\\u001c", "\\u001d", "\\u001e", "\\u001f"}; +// unoptimized, meant for compile-time execution +consteval std::string consteval_to_quoted_escaped(std::string_view input) { + std::string out = "\""; + for (char c : input) { + if (json_quotable_character[uint8_t(c)]) { + if (c == '"') { + out.append("\\\""); + } else if (c == '\\') { + out.append("\\\\"); + } else { + std::string_view v = control_chars[uint8_t(c)]; + out.append(v); + } + } else { + out.push_back(c); + } + } + out.push_back('"'); + return out; +} +#endif // SIMDJSON_CONSTEVAL + + +#if SIMDJSON_SUPPORTS_CONCEPTS +template +struct fixed_string { + constexpr fixed_string(const char (&str)[N]) { + for (std::size_t i = 0; i < N; ++i) { + data[i] = str[i]; + } + } + char data[N]; + constexpr std::string_view view() const { return {data, N - 1}; } +}; +template +fixed_string(const char (&)[N]) -> fixed_string; + +template +struct string_constant { + static constexpr std::string_view value = str.view(); +}; +#endif // SIMDJSON_SUPPORTS_CONCEPTS + +} // namespace constevalutil +} // namespace simdjson +#endif // SIMDJSON_CONSTEVALUTIL_H +/* end file simdjson/constevalutil.h */ /** * @brief The top level simdjson namespace, containing everything the library provides. @@ -3071,8 +3317,38 @@ simdjson_inline error_code simdjson_result_base::error() const noexcept { return this->second; } + +template +simdjson_inline bool simdjson_result_base::has_value() const noexcept { + return this->error() == SUCCESS; +} + #if SIMDJSON_EXCEPTIONS + +template +simdjson_inline T& simdjson_result_base::operator*() & noexcept(false) { + return this->value(); +} + +template +simdjson_inline T&& simdjson_result_base::operator*() && noexcept(false) { + return std::forward>(*this).value(); +} + +template +simdjson_inline T* simdjson_result_base::operator->() noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + + +template +simdjson_inline const T* simdjson_result_base::operator->() const noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + template simdjson_inline T& simdjson_result_base::value() & noexcept(false) { if (error()) { throw simdjson_error(error()); } @@ -3097,6 +3373,7 @@ simdjson_inline simdjson_result_base::operator T&&() && noexcept(false) { #endif // SIMDJSON_EXCEPTIONS + template simdjson_inline const T& simdjson_result_base::value_unsafe() const& noexcept { return this->first; @@ -3131,26 +3408,10 @@ simdjson_inline void simdjson_result::tie(T &value, error_code &error) && noe std::forward>(*this).tie(value, error); } -template -simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &value) && noexcept { - return std::forward>(*this).get(value); -} - template simdjson_warn_unused simdjson_inline error_code -simdjson_result::get(std::string &value) && noexcept -#if SIMDJSON_SUPPORTS_DESERIALIZATION -requires (!std::is_same_v) -#endif // SIMDJSON_SUPPORTS_DESERIALIZATION -{ - // SFINAE : n'active que pour T = std::string_view - static_assert(std::is_same::value, "simdjson_result::get(std::string&) n'est disponible que pour T = std::string_view"); - std::string_view v; - error_code error = std::forward>(*this).get(v); - if (!error) { - value.assign(v.data(), v.size()); - } - return error; +simdjson_result::get(T &value) && noexcept { + return std::forward>(*this).get(value); } template @@ -4016,6 +4277,9 @@ class padded_string_view : public std::string_view { /** The number of allocated bytes. */ inline size_t capacity() const noexcept; + /** check that the view has sufficient padding */ + inline bool has_sufficient_padding() const noexcept; + /** * Remove the UTF-8 Byte Order Mark (BOM) if it exists. * @@ -4042,14 +4306,24 @@ inline std::ostream& operator<<(std::ostream& out, simdjson_result= SIMDJSON_PADDING) { + return true; + } + size_t missing_padding = SIMDJSON_PADDING - padding(); + if(length() < missing_padding) { return false; } + + for (size_t i = length() - missing_padding; i < length(); i++) { + char c = data()[i]; + if (c != ' ' && c != '\t' && c != '\n' && c != '\r') { + return false; + } + } + return true; +} + inline size_t padded_string_view::capacity() const noexcept { return _capacity; } inline size_t padded_string_view::padding() const noexcept { return capacity() - length(); } @@ -4114,10 +4404,33 @@ inline std::ostream& operator<<(std::ostream& out, simdjson_result 0; i--) { + char c = s[i - 1]; + if (c == ' ' || c == '\t' || c == '\n' || c == '\r') { + existing_padding++; + } else { + break; + } + } + size_t needed_padding = 0; + if (existing_padding < SIMDJSON_PADDING) { + needed_padding = SIMDJSON_PADDING - existing_padding; + s.append(needed_padding, ' '); + } + + return padded_string_view(s.data(), s.size() - needed_padding, s.size()); +} + +inline padded_string_view pad_with_reserve(std::string& s) noexcept { + if (s.capacity() - s.size() < SIMDJSON_PADDING) { + s.reserve(s.size() + SIMDJSON_PADDING ); + } + return padded_string_view(s.data(), s.size(), s.capacity()); } + + + } // namespace simdjson @@ -4242,9 +4555,9 @@ inline const char *padded_string::data() const noexcept { return data_ptr; } inline char *padded_string::data() noexcept { return data_ptr; } -inline padded_string::operator std::string_view() const { return std::string_view(data(), length()); } +inline padded_string::operator std::string_view() const simdjson_lifetime_bound { return std::string_view(data(), length()); } -inline padded_string::operator padded_string_view() const noexcept { +inline padded_string::operator padded_string_view() const noexcept simdjson_lifetime_bound { return padded_string_view(data(), length(), length() + SIMDJSON_PADDING); } @@ -4384,6 +4697,8 @@ class tape_ref; #ifndef SIMDJSON_DOM_ARRAY_H #define SIMDJSON_DOM_ARRAY_H +#include + /* skipped duplicate #include "simdjson/dom/base.h" */ /* including simdjson/internal/tape_ref.h: #include "simdjson/internal/tape_ref.h" */ /* begin file simdjson/internal/tape_ref.h */ @@ -4491,7 +4806,7 @@ class array { iterator& operator=(const iterator&) noexcept = default; private: simdjson_inline iterator(const internal::tape_ref &tape) noexcept; - internal::tape_ref tape; + internal::tape_ref tape{}; friend class array; }; @@ -4542,6 +4857,17 @@ class array { */ inline simdjson_result at_pointer(std::string_view json_pointer) const noexcept; + /** + * Recursive function which processes the json path of each child element + */ + inline void process_json_path_of_child_elements(std::vector::iterator& current, std::vector::iterator& end, const std::string_view& path_suffix, std::vector& accumulator) const noexcept; + + + /** + * Adds support for JSONPath expression with wildcards '*' + */ + inline simdjson_result> at_path_with_wildcard(std::string_view json_path) const noexcept; + /** * Get the value associated with the given JSONPath expression. We only support * JSONPath queries that trivially convertible to JSON Pointer queries: key @@ -4575,6 +4901,15 @@ class array { */ inline simdjson_result at(size_t index) const noexcept; + /** + * Gets the values of items in an array element + * This function has linear-time complexity: the values are checked one by one. + * + * @return The child elements of an array + */ + + inline std::vector& get_values(std::vector& out) const noexcept; + /** * Implicitly convert object to element */ @@ -4582,7 +4917,7 @@ class array { private: simdjson_inline array(const internal::tape_ref &tape) noexcept; - internal::tape_ref tape; + internal::tape_ref tape{}; friend class element; friend struct simdjson_result; template @@ -4601,8 +4936,11 @@ struct simdjson_result : public internal::simdjson_result_base at_pointer(std::string_view json_pointer) const noexcept; + inline void process_json_path_of_child_elements(std::vector::iterator& current, std::vector::iterator& end, const std::string_view& path_suffix, std::vector& accumulator) const noexcept; + inline simdjson_result> at_path_with_wildcard(std::string_view json_path) const noexcept; inline simdjson_result at_path(std::string_view json_path) const noexcept; inline simdjson_result at(size_t index) const noexcept; + inline std::vector& get_values(std::vector& out) const noexcept; #if SIMDJSON_EXCEPTIONS inline dom::array::iterator begin() const noexcept(false); @@ -4615,9 +4953,7 @@ struct simdjson_result : public internal::simdjson_result_base - +#if SIMDJSON_SUPPORTS_RANGES namespace std { namespace ranges { template<> @@ -4628,7 +4964,7 @@ inline constexpr bool enable_view load(const std::string &path) & noexcept; - inline simdjson_result load(const std::string &path) && = delete ; + inline simdjson_result load(std::string_view path) & noexcept; + inline simdjson_result load(std::string_view path) && = delete ; /** * Load a JSON document from a file into a provide document instance and return a temporary reference to it. @@ -4883,8 +5219,8 @@ class parser { * - other json errors if parsing fails. You should not rely on these errors to always the same for the * same document: they may vary under runtime dispatch (so they may vary depending on your system and hardware). */ - inline simdjson_result load_into_document(document& doc, const std::string &path) & noexcept; - inline simdjson_result load_into_document(document& doc, const std::string &path) && =delete; + inline simdjson_result load_into_document(document& doc, std::string_view path) & noexcept; + inline simdjson_result load_into_document(document& doc, std::string_view path) && =delete; /** * Parse a JSON document and return a temporary reference to it. @@ -5121,7 +5457,7 @@ class parser { * - other json errors if parsing fails. You should not rely on these errors to always the same for the * same document: they may vary under runtime dispatch (so they may vary depending on your system and hardware). */ - inline simdjson_result load_many(const std::string &path, size_t batch_size = dom::DEFAULT_BATCH_SIZE) noexcept; + inline simdjson_result load_many(std::string_view path, size_t batch_size = dom::DEFAULT_BATCH_SIZE) noexcept; /** * Parse a buffer containing many JSON documents. @@ -5392,7 +5728,7 @@ class parser { inline error_code ensure_capacity(document& doc, size_t desired_capacity) noexcept; /** Read the file into loaded_bytes */ - inline simdjson_result read_file(const std::string &path) noexcept; + inline simdjson_result read_file(std::string_view path) noexcept; friend class parser::Iterator; friend class document_stream; @@ -5729,6 +6065,8 @@ struct simdjson_result : public internal::simdjson_result_ #ifndef SIMDJSON_DOM_ELEMENT_H #define SIMDJSON_DOM_ELEMENT_H +#include + /* skipped duplicate #include "simdjson/dom/base.h" */ /* skipped duplicate #include "simdjson/dom/array.h" */ @@ -6127,6 +6465,8 @@ class element { */ inline simdjson_result at_pointer(const std::string_view json_pointer) const noexcept; + inline simdjson_result> at_path_with_wildcard(const std::string_view json_path) const noexcept; + /** * Get the value associated with the given JSONPath expression. We only support * JSONPath queries that trivially convertible to JSON Pointer queries: key @@ -6220,7 +6560,7 @@ class element { private: simdjson_inline element(const internal::tape_ref &tape) noexcept; - internal::tape_ref tape; + internal::tape_ref tape{}; friend class document; friend class object; friend class array; @@ -6272,6 +6612,7 @@ struct simdjson_result : public internal::simdjson_result_base operator[](const char *key) const noexcept; simdjson_result operator[](int) const noexcept = delete; simdjson_inline simdjson_result at_pointer(const std::string_view json_pointer) const noexcept; + simdjson_inline simdjson_result> at_path_with_wildcard(const std::string_view json_path) const noexcept; simdjson_inline simdjson_result at_path(const std::string_view json_path) const noexcept; [[deprecated("For standard compliance, use at_pointer instead, and prefix your pointers with a slash '/', see RFC6901 ")]] simdjson_inline simdjson_result at(const std::string_view json_pointer) const noexcept; @@ -6303,6 +6644,8 @@ struct simdjson_result : public internal::simdjson_result_base + /* skipped duplicate #include "simdjson/dom/base.h" */ /* skipped duplicate #include "simdjson/dom/element.h" */ /* skipped duplicate #include "simdjson/internal/tape_ref.h" */ @@ -6391,7 +6734,7 @@ class object { private: simdjson_inline iterator(const internal::tape_ref &tape) noexcept; - internal::tape_ref tape; + internal::tape_ref tape{}; friend class object; }; @@ -6474,6 +6817,16 @@ class object { */ inline simdjson_result at_pointer(std::string_view json_pointer) const noexcept; + /** + * Recursive function which processes the json path of each child element + */ + inline void process_json_path_of_child_elements(std::vector::iterator& current, std::vector::iterator& end, const std::string_view& path_suffix, std::vector& accumulator) const noexcept; + + /** + * Adds support for JSONPath expression with wildcards '*' + */ + inline simdjson_result> at_path_with_wildcard(std::string_view json_path) const noexcept; + /** * Get the value associated with the given JSONPath expression. We only support * JSONPath queries that trivially convertible to JSON Pointer queries: key @@ -6505,6 +6858,14 @@ class object { */ inline simdjson_result at_key(std::string_view key) const noexcept; + /** + * Gets the values associated with keys of an object + * This function has linear-time complexity: the keys are checked one by one. + * + * @return the values associated with each key of an object + */ + inline std::vector& get_values(std::vector& out) const noexcept; + /** * Get the value associated with the given key in a case-insensitive manner. * It is only guaranteed to work over ASCII inputs. @@ -6526,7 +6887,7 @@ class object { private: simdjson_inline object(const internal::tape_ref &tape) noexcept; - internal::tape_ref tape; + internal::tape_ref tape{}; friend class element; friend struct simdjson_result; @@ -6563,8 +6924,11 @@ struct simdjson_result : public internal::simdjson_result_base operator[](const char *key) const noexcept; simdjson_result operator[](int) const noexcept = delete; inline simdjson_result at_pointer(std::string_view json_pointer) const noexcept; + inline void process_json_path_of_child_elements(std::vector::iterator& current, std::vector::iterator& end, const std::string_view& path_suffix, std::vector& accumulator) const noexcept; + inline simdjson_result> at_path_with_wildcard(std::string_view json_path_new) const noexcept; inline simdjson_result at_path(std::string_view json_path) const noexcept; inline simdjson_result at_key(std::string_view key) const noexcept; + inline std::vector& get_values(std::vector& out) const noexcept; inline simdjson_result at_key_case_insensitive(std::string_view key) const noexcept; #if SIMDJSON_EXCEPTIONS @@ -6576,9 +6940,7 @@ struct simdjson_result : public internal::simdjson_result_base - +#if SIMDJSON_SUPPORTS_RANGES namespace std { namespace ranges { template<> @@ -6589,7 +6951,7 @@ inline constexpr bool enable_view - namespace simdjson { /** @@ -6614,8 +6974,7 @@ namespace simdjson { */ namespace internal { -template -class base_formatter { +template class base_formatter { public: /** Add a comma **/ simdjson_inline void comma(); @@ -6654,24 +7013,76 @@ class base_formatter { /** Prints one character **/ simdjson_inline void one_char(char c); + /** Prints characters in [begin, end) verbatim. **/ + simdjson_inline void chars(const char *begin, const char *end); + simdjson_inline void call_print_newline() { - static_cast(this)->print_newline(); + static_cast(this)->print_newline(); } simdjson_inline void call_print_indents(size_t depth) { - static_cast(this)->print_indents(depth); + static_cast(this)->print_indents(depth); } simdjson_inline void call_print_space() { - static_cast(this)->print_space(); + static_cast(this)->print_space(); } protected: // implementation details (subject to change) /** Backing buffer **/ - std::vector buffer{}; // not ideal! -}; + struct vector_with_small_buffer { + vector_with_small_buffer() = default; + ~vector_with_small_buffer() { free_buffer(); } + vector_with_small_buffer(const vector_with_small_buffer &) = delete; + vector_with_small_buffer & + operator=(const vector_with_small_buffer &) = delete; + + void clear() { + size = 0; + capacity = StaticCapacity; + free_buffer(); + buffer = array; + } + + simdjson_inline void push_back(char c) { + if (capacity < size + 1) + grow(capacity * 2); + buffer[size++] = c; + } + + simdjson_inline void append(const char *begin, const char *end) { + const size_t new_size = size + (end - begin); + if (capacity < new_size) + // std::max(new_size, capacity * 2); is broken in tests on Windows + grow(new_size < capacity * 2 ? capacity * 2 : new_size); + std::copy(begin, end, buffer + size); + size = new_size; + } + + std::string_view str() const { return std::string_view(buffer, size); } + + private: + void free_buffer() { + if (buffer != array) + delete[] buffer; + } + void grow(size_t new_capacity) { + auto new_buffer = new char[new_capacity]; + std::copy(buffer, buffer + size, new_buffer); + free_buffer(); + buffer = new_buffer; + capacity = new_capacity; + } + + static const size_t StaticCapacity = 64; + char array[StaticCapacity]; + char *buffer = array; + size_t size = 0; + size_t capacity = StaticCapacity; + } buffer{}; +}; /** * @private This is the class that we expect to use with the string_builder @@ -6705,9 +7116,11 @@ class pretty_formatter : public base_formatter { * by a "formatter" which handles the details. Thus * the string_builder template could support both minification * and prettification, and various other tradeoffs. + * + * This is not to be confused with the simdjson::builder::string_builder + * which is a different class. */ -template -class string_builder { +template class string_builder { public: /** Construct an initially empty builder, would print the empty string **/ string_builder() = default; @@ -6729,11 +7142,12 @@ class string_builder { simdjson_inline std::string_view str() const; /** Append a key_value_pair to the builder (to be printed) **/ simdjson_inline void append(simdjson::dom::key_value_pair value); + private: formatter format{}; }; -} // internal +} // namespace internal namespace dom { @@ -6742,33 +7156,43 @@ namespace dom { * * @param out The output stream. * @param value The element. - * @throw if there is an error with the underlying output stream. simdjson itself will not throw. + * @throw if there is an error with the underlying output stream. simdjson + * itself will not throw. */ -inline std::ostream& operator<<(std::ostream& out, simdjson::dom::element value); +inline std::ostream &operator<<(std::ostream &out, + simdjson::dom::element value); #if SIMDJSON_EXCEPTIONS -inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result x); +inline std::ostream & +operator<<(std::ostream &out, + simdjson::simdjson_result x); #endif /** * Print JSON to an output stream. * * @param out The output stream. * @param value The array. - * @throw if there is an error with the underlying output stream. simdjson itself will not throw. + * @throw if there is an error with the underlying output stream. simdjson + * itself will not throw. */ -inline std::ostream& operator<<(std::ostream& out, simdjson::dom::array value); +inline std::ostream &operator<<(std::ostream &out, simdjson::dom::array value); #if SIMDJSON_EXCEPTIONS -inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result x); +inline std::ostream & +operator<<(std::ostream &out, + simdjson::simdjson_result x); #endif /** * Print JSON to an output stream. * * @param out The output stream. * @param value The object. - * @throw if there is an error with the underlying output stream. simdjson itself will not throw. + * @throw if there is an error with the underlying output stream. simdjson + * itself will not throw. */ -inline std::ostream& operator<<(std::ostream& out, simdjson::dom::object value); +inline std::ostream &operator<<(std::ostream &out, simdjson::dom::object value); #if SIMDJSON_EXCEPTIONS -inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result x); +inline std::ostream & +operator<<(std::ostream &out, + simdjson::simdjson_result x); #endif } // namespace dom @@ -6780,47 +7204,47 @@ inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result -std::string to_string(T x) { - // in C++, to_string is standard: http://www.cplusplus.com/reference/string/to_string/ - // Currently minify and to_string are identical but in the future, they may - // differ. - simdjson::internal::string_builder<> sb; - sb.append(x); - std::string_view answer = sb.str(); - return std::string(answer.data(), answer.size()); +template std::string to_string(T x) { + // in C++, to_string is standard: + // http://www.cplusplus.com/reference/string/to_string/ Currently minify and + // to_string are identical but in the future, they may differ. + simdjson::internal::string_builder<> sb; + sb.append(x); + std::string_view answer = sb.str(); + return std::string(answer.data(), answer.size()); } #if SIMDJSON_EXCEPTIONS -template -std::string to_string(simdjson_result x) { - if (x.error()) { throw simdjson_error(x.error()); } - return to_string(x.value()); +template std::string to_string(simdjson_result x) { + if (x.error()) { + throw simdjson_error(x.error()); + } + return to_string(x.value()); } #endif /** - * Minifies a JSON element or document, printing the smallest possible valid JSON. + * Minifies a JSON element or document, printing the smallest possible valid + * JSON. * * dom::parser parser; * element doc = parser.parse(" [ 1 , 2 , 3 ] "_padded); * cout << minify(doc) << endl; // prints [1,2,3] * */ -template -std::string minify(T x) { - return to_string(x); -} +template std::string minify(T x) { return to_string(x); } #if SIMDJSON_EXCEPTIONS -template -std::string minify(simdjson_result x) { - if (x.error()) { throw simdjson_error(x.error()); } - return to_string(x.value()); +template std::string minify(simdjson_result x) { + if (x.error()) { + throw simdjson_error(x.error()); + } + return to_string(x.value()); } #endif /** - * Prettifies a JSON element or document, printing the valid JSON with indentation. + * Prettifies a JSON element or document, printing the valid JSON with + * indentation. * * dom::parser parser; * element doc = parser.parse(" [ 1 , 2 , 3 ] "_padded); @@ -6836,25 +7260,24 @@ std::string minify(simdjson_result x) { * cout << prettify(doc) << endl; * */ -template -std::string prettify(T x) { - simdjson::internal::string_builder sb; - sb.append(x); - std::string_view answer = sb.str(); - return std::string(answer.data(), answer.size()); +template std::string prettify(T x) { + simdjson::internal::string_builder sb; + sb.append(x); + std::string_view answer = sb.str(); + return std::string(answer.data(), answer.size()); } #if SIMDJSON_EXCEPTIONS -template -std::string prettify(simdjson_result x) { - if (x.error()) { throw simdjson_error(x.error()); } - return to_string(x.value()); +template std::string prettify(simdjson_result x) { + if (x.error()) { + throw simdjson_error(x.error()); + } + return to_string(x.value()); } #endif } // namespace simdjson - #endif /* end file simdjson/dom/serialization.h */ @@ -6878,6 +7301,8 @@ std::string prettify(simdjson_result x) { #include /* skipped duplicate #include "simdjson/common_defs.h" */ +#include + namespace simdjson { /** * Converts JSONPath to JSON Pointer. @@ -6886,12 +7311,12 @@ namespace simdjson { */ inline std::string json_path_to_pointer_conversion(std::string_view json_path) { size_t i = 0; - // if JSONPath starts with $, skip it + // json_path.starts_with('$') requires C++20. if (!json_path.empty() && json_path.front() == '$') { i = 1; } - if (json_path.empty() || (json_path[i] != '.' && + if (i >= json_path.size() || (json_path[i] != '.' && json_path[i] != '[')) { return "-1"; // This is just a sentinel value, the caller should check for this and return an error. } @@ -6934,6 +7359,49 @@ inline std::string json_path_to_pointer_conversion(std::string_view json_path) { return result; } + +inline std::pair get_next_key_and_json_path(std::string_view& json_path) { + std::string_view key; + + if (json_path.empty()) { + return {key, json_path}; + } + size_t i = 0; + + // if JSONPath starts with $, skip it + if (json_path.front() == '$') { + i = 1; + } + + + if (i < json_path.length() && json_path[i] == '.') { + i += 1; + size_t key_start = i; + + while (i < json_path.length() && json_path[i] != '[' && json_path[i] != '.') { + ++i; + } + + key = json_path.substr(key_start, i - key_start); + } else if ((i+1 < json_path.size()) && json_path[i] == '[' && (json_path[i+1] == '\'' || json_path[i+1] == '"')) { + i += 2; + size_t key_start = i; + while (i < json_path.length() && json_path[i] != '\'' && json_path[i] != '"') { + ++i; + } + + key = json_path.substr(key_start, i - key_start); + + i += 2; + } else if ((i+2 < json_path.size()) && json_path[i] == '[' && json_path[i+1] == '*' && json_path[i+2] == ']') { // i.e [*].additional_keys or [*]["additional_keys"] + key = "*"; + i += 3; + } + + + return std::make_pair(key, json_path.substr(i)); +} + } // namespace simdjson #endif // SIMDJSON_JSONPATHUTIL_H /* end file simdjson/jsonpathutil.h */ @@ -7131,11 +7599,22 @@ inline simdjson_result simdjson_result::at_pointer(std return at_pointer(json_pointer); } +inline simdjson_result> simdjson_result::at_path_with_wildcard(std::string_view json_path) const noexcept { + if (error()) { + return error(); + } + return first.at_path_with_wildcard(json_path); +} + inline simdjson_result simdjson_result::at(size_t index) const noexcept { if (error()) { return error(); } return first.at(index); } +inline std::vector& simdjson_result::get_values(std::vector& out) const noexcept { + return first.get_values(out); +} + namespace dom { // @@ -7206,6 +7685,93 @@ inline simdjson_result array::at_path(std::string_view json_path) const return at_pointer(json_pointer); } +inline void array::process_json_path_of_child_elements(std::vector::iterator& current, std::vector::iterator& end, const std::string_view& path_suffix, std::vector& accumulator) const noexcept { + if (current == end) { + return; + } + + simdjson_result> result; + + + for (auto it = current; it != end; ++it) { + std::vector child_result; + auto error = it->at_path_with_wildcard(path_suffix).get(child_result); + if(error) { + continue; + } + accumulator.reserve(accumulator.size() + child_result.size()); + accumulator.insert(accumulator.end(), + std::make_move_iterator(child_result.begin()), + std::make_move_iterator(child_result.end())); + } +} + +inline simdjson_result> array::at_path_with_wildcard(std::string_view json_path) const noexcept { + SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914 + + size_t i = 0; + // json_path.starts_with('$') requires C++20. + if (!json_path.empty() && json_path.front() == '$') { + i = 1; + } + + if (i >= json_path.size() || (json_path[i] != '.' && json_path[i] != '[')) { + return INVALID_JSON_POINTER; + } + + if (json_path.find("*") != std::string::npos) { + std::vector child_values; + + if ( + (json_path.compare(i, 3, "[*]") == 0 && json_path.size() == i + 3) || + (json_path.compare(i, 2,".*") == 0 && json_path.size() == i + 2) + ) { + get_values(child_values); + return child_values; + } + + std::pair key_and_json_path = get_next_key_and_json_path(json_path); + + std::string_view key = key_and_json_path.first; + json_path = key_and_json_path.second; + + if (key.size() > 0) { + if (key == "*") { + get_values(child_values); + } else { + element pointer_result; + std::string json_pointer = std::string("/") + std::string(key); + auto error = at_pointer(json_pointer).get(pointer_result); + + if (!error) { + child_values.emplace_back(pointer_result); + } + } + + std::vector result = {}; + + if (child_values.size() > 0) { + std::vector::iterator child_values_begin = child_values.begin(); + std::vector::iterator child_values_end = child_values.end(); + + process_json_path_of_child_elements(child_values_begin, child_values_end, json_path, result); + } + + return result; + } else { + return INVALID_JSON_POINTER; + } + } else { + element result; + auto error = at_path(json_path).get(result); + if (error) { + return error; + } + + return std::vector{std::move(result)}; + } +} + inline simdjson_result array::at(size_t index) const noexcept { SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914 size_t i=0; @@ -7216,6 +7782,15 @@ inline simdjson_result array::at(size_t index) const noexcept { return INDEX_OUT_OF_BOUNDS; } +inline std::vector& array::get_values(std::vector& out) const noexcept { + out.reserve(this->size()); + for (auto element : *this) { + out.emplace_back(element); + } + + return out; +} + inline array::operator element() const noexcept { return element(tape); } @@ -7315,10 +7890,19 @@ inline simdjson_result simdjson_result::at_path(std:: if (json_pointer == "-1") { return INVALID_JSON_POINTER; } return at_pointer(json_pointer); } +inline simdjson_result> simdjson_result::at_path_with_wildcard(std::string_view json_path) const noexcept { + if (error()) { + return error(); + } + return first.at_path_with_wildcard(json_path); +} inline simdjson_result simdjson_result::at_key(std::string_view key) const noexcept { if (error()) { return error(); } return first.at_key(key); } +inline std::vector& simdjson_result::get_values(std::vector& out) const noexcept { + return first.get_values(out); +} inline simdjson_result simdjson_result::at_key_case_insensitive(std::string_view key) const noexcept { if (error()) { return error(); } return first.at_key_case_insensitive(key); @@ -7418,6 +8002,97 @@ inline simdjson_result object::at_path(std::string_view json_path) cons return at_pointer(json_pointer); } +inline void object::process_json_path_of_child_elements(std::vector::iterator& current, std::vector::iterator& end, const std::string_view& path_suffix, std::vector& accumulator) const noexcept { + if (current == end) { + return; + } + + simdjson_result> result; + + for (auto it = current; it != end; ++it) { + std::vector child_result; + auto error = it->at_path_with_wildcard(path_suffix).get(child_result); + if(error) { + continue; + } + accumulator.reserve(accumulator.size() + child_result.size()); + accumulator.insert(accumulator.end(), + std::make_move_iterator(child_result.begin()), + std::make_move_iterator(child_result.end())); + } +} + +inline simdjson_result> object::at_path_with_wildcard(std::string_view json_path) const noexcept { + SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914 + + size_t i = 0; + if (json_path.empty()) { + return INVALID_JSON_POINTER; + } + // if JSONPath starts with $, skip it + // json_path.starts_with('$') requires C++20. + if (json_path.front() == '$') { + i = 1; + } + + if (i >= json_path.size() || (json_path[i] != '.' && json_path[i] != '[')) { + // expect json path to always start with $ but this isn't currently + // expected in jsonpathutil.h. + return INVALID_JSON_POINTER; + } + + if (json_path.find("*") != std::string::npos) { + + std::vector child_values; + + if ( + (json_path.compare(i, 3, "[*]") == 0 && json_path.size() == i + 3) || + (json_path.compare(i, 2,".*") == 0 && json_path.size() == i + 2) + ) { + get_values(child_values); + return child_values; + } + + std::pair key_and_json_path = get_next_key_and_json_path(json_path); + + std::string_view key = key_and_json_path.first; + json_path = key_and_json_path.second; + + if (key.size() > 0) { + if (key == "*") { + get_values(child_values); + } else { + element pointer_result; + auto error = at_pointer(std::string("/") + std::string(key)).get(pointer_result); + + if (!error) { + child_values.emplace_back(pointer_result); + } + } + + std::vector result = {}; + if (child_values.size() > 0) { + + std::vector::iterator child_values_begin = child_values.begin(); + std::vector::iterator child_values_end = child_values.end(); + + process_json_path_of_child_elements(child_values_begin, child_values_end, json_path, result); + } + + return result; + } else { + return INVALID_JSON_POINTER; + } + } else { + element result; + auto error = this->at_path(json_path).get(result); + if (error) { + return error; + } + return std::vector{std::move(result)}; + } +} + inline simdjson_result object::at_key(std::string_view key) const noexcept { iterator end_field = end(); for (iterator field = begin(); field != end_field; ++field) { @@ -7427,6 +8102,18 @@ inline simdjson_result object::at_key(std::string_view key) const noexc } return NO_SUCH_FIELD; } + +inline std::vector& object::get_values(std::vector& out) const noexcept { + iterator end_field = end(); + iterator begin_field = begin(); + + out.reserve(std::distance(begin_field, end_field)); + for (iterator field = begin_field; field != end_field; ++field) { + out.emplace_back(field.value()); + } + + return out; +} // In case you wonder why we need this, please see // https://github.com/simdjson/simdjson/issues/323 // People do seek keys in a case-insensitive manner. @@ -7538,14 +8225,14 @@ inline key_value_pair::key_value_pair(std::string_view _key, element _value) noe } // namespace simdjson -#if defined(__cpp_lib_ranges) +#if SIMDJSON_SUPPORTS_RANGES static_assert(std::ranges::view); static_assert(std::ranges::sized_range); #if SIMDJSON_EXCEPTIONS static_assert(std::ranges::view>); static_assert(std::ranges::sized_range>); #endif // SIMDJSON_EXCEPTIONS -#endif // defined(__cpp_lib_ranges) +#endif // SIMDJSON_SUPPORTS_RANGES #endif // SIMDJSON_OBJECT_INL_H /* end file simdjson/dom/object-inl.h */ @@ -7669,6 +8356,12 @@ simdjson_inline simdjson_result simdjson_result::at_ if (json_pointer == "-1") { return INVALID_JSON_POINTER; } return at_pointer(json_pointer); } + +simdjson_inline simdjson_result> simdjson_result::at_path_with_wildcard(const std::string_view json_path) const noexcept { + if (error()) { return error(); } + return first.at_path_with_wildcard(json_path); +} + #ifndef SIMDJSON_DISABLE_DEPRECATED_API [[deprecated("For standard compliance, use at_pointer instead, and prefix your pointers with a slash '/', see RFC6901 ")]] simdjson_inline simdjson_result simdjson_result::at(const std::string_view json_pointer) const noexcept { @@ -7959,6 +8652,20 @@ inline simdjson_result element::at_pointer(std::string_view json_pointe } } } + +inline simdjson_result> element::at_path_with_wildcard(std::string_view json_path) const noexcept { + SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914 + + switch (tape.tape_ref_type()) { + case internal::tape_type::START_OBJECT: + return object(tape).at_path_with_wildcard(json_path); + case internal::tape_type::START_ARRAY: + return array(tape).at_path_with_wildcard(json_path); + default: + return std::vector{}; + } +} + inline simdjson_result element::at_path(std::string_view json_path) const noexcept { auto json_pointer = json_path_to_pointer_conversion(json_path); if (json_pointer == "-1") { return INVALID_JSON_POINTER; } @@ -8025,14 +8732,14 @@ inline std::ostream& operator<<(std::ostream& out, element_type type) { #endif // SIMDJSON_ELEMENT_INL_H /* end file simdjson/dom/element-inl.h */ -#if defined(__cpp_lib_ranges) +#if SIMDJSON_SUPPORTS_RANGES static_assert(std::ranges::view); static_assert(std::ranges::sized_range); #if SIMDJSON_EXCEPTIONS static_assert(std::ranges::view>); static_assert(std::ranges::sized_range>); #endif // SIMDJSON_EXCEPTIONS -#endif // defined(__cpp_lib_ranges) +#endif // SIMDJSON_SUPPORTS_RANGES #endif // SIMDJSON_ARRAY_INL_H /* end file simdjson/dom/array-inl.h */ @@ -8083,11 +8790,11 @@ inline bool parser::dump_raw_tape(std::ostream &os) const noexcept { return valid ? doc.dump_raw_tape(os) : false; } -inline simdjson_result parser::read_file(const std::string &path) noexcept { +inline simdjson_result parser::read_file(std::string_view path) noexcept { // Open the file SIMDJSON_PUSH_DISABLE_WARNINGS SIMDJSON_DISABLE_DEPRECATED_WARNING // Disable CRT_SECURE warning on MSVC: manually verified this is safe - std::FILE *fp = std::fopen(path.c_str(), "rb"); + std::FILE *fp = std::fopen(path.data(), "rb"); SIMDJSON_POP_DISABLE_WARNINGS if (fp == nullptr) { @@ -8139,18 +8846,18 @@ inline simdjson_result parser::read_file(const std::string &path) noexce return bytes_read; } -inline simdjson_result parser::load(const std::string &path) & noexcept { +inline simdjson_result parser::load(std::string_view path) & noexcept { return load_into_document(doc, path); } -inline simdjson_result parser::load_into_document(document& provided_doc, const std::string &path) & noexcept { +inline simdjson_result parser::load_into_document(document& provided_doc, std::string_view path) & noexcept { size_t len; auto _error = read_file(path).get(len); if (_error) { return _error; } return parse_into_document(provided_doc, loaded_bytes.get(), len, false); } -inline simdjson_result parser::load_many(const std::string &path, size_t batch_size) noexcept { +inline simdjson_result parser::load_many(std::string_view path, size_t batch_size) noexcept { size_t len; auto _error = read_file(path).get(len); if (_error) { return _error; } @@ -8886,8 +9593,8 @@ inline bool document::dump_raw_tape(std::ostream &os) const noexcept { #define SIMDJSON_SERIALIZATION_INL_H /* skipped duplicate #include "simdjson/dom/base.h" */ -/* skipped duplicate #include "simdjson/dom/serialization.h" */ /* skipped duplicate #include "simdjson/dom/parser.h" */ +/* skipped duplicate #include "simdjson/dom/serialization.h" */ /* skipped duplicate #include "simdjson/internal/tape_type.h" */ /* skipped duplicate #include "simdjson/dom/array-inl.h" */ @@ -8899,7 +9606,9 @@ inline bool document::dump_raw_tape(std::ostream &os) const noexcept { namespace simdjson { namespace dom { inline bool parser::print_json(std::ostream &os) const noexcept { - if (!valid) { return false; } + if (!valid) { + return false; + } simdjson::internal::string_builder<> sb; sb.append(doc.root()); std::string_view answer = sb.str(); @@ -8907,37 +9616,51 @@ inline bool parser::print_json(std::ostream &os) const noexcept { return true; } -inline std::ostream& operator<<(std::ostream& out, simdjson::dom::element value) { - simdjson::internal::string_builder<> sb; - sb.append(value); - return (out << sb.str()); +inline std::ostream &operator<<(std::ostream &out, + simdjson::dom::element value) { + simdjson::internal::string_builder<> sb; + sb.append(value); + return (out << sb.str()); } #if SIMDJSON_EXCEPTIONS -inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result x) { - if (x.error()) { throw simdjson::simdjson_error(x.error()); } - return (out << x.value()); +inline std::ostream & +operator<<(std::ostream &out, + simdjson::simdjson_result x) { + if (x.error()) { + throw simdjson::simdjson_error(x.error()); + } + return (out << x.value()); } #endif -inline std::ostream& operator<<(std::ostream& out, simdjson::dom::array value) { - simdjson::internal::string_builder<> sb; - sb.append(value); - return (out << sb.str()); +inline std::ostream &operator<<(std::ostream &out, simdjson::dom::array value) { + simdjson::internal::string_builder<> sb; + sb.append(value); + return (out << sb.str()); } #if SIMDJSON_EXCEPTIONS -inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result x) { - if (x.error()) { throw simdjson::simdjson_error(x.error()); } - return (out << x.value()); +inline std::ostream & +operator<<(std::ostream &out, + simdjson::simdjson_result x) { + if (x.error()) { + throw simdjson::simdjson_error(x.error()); + } + return (out << x.value()); } #endif -inline std::ostream& operator<<(std::ostream& out, simdjson::dom::object value) { - simdjson::internal::string_builder<> sb; - sb.append(value); - return (out << sb.str()); +inline std::ostream &operator<<(std::ostream &out, + simdjson::dom::object value) { + simdjson::internal::string_builder<> sb; + sb.append(value); + return (out << sb.str()); } #if SIMDJSON_EXCEPTIONS -inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result x) { - if (x.error()) { throw simdjson::simdjson_error(x.error()); } - return (out << x.value()); +inline std::ostream & +operator<<(std::ostream &out, + simdjson::simdjson_result x) { + if (x.error()) { + throw simdjson::simdjson_error(x.error()); + } + return (out << x.value()); } #endif @@ -8952,8 +9675,9 @@ namespace { * We expect that most compilers will use 8 bytes for this data structure. **/ struct escape_sequence { - uint8_t length; - const char string[7]; // technically, we only ever need 6 characters, we pad to 8 + uint8_t length; + const char + string[7]; // technically, we only ever need 6 characters, we pad to 8 }; /**@private * This converts a signed integer into a character sequence. @@ -8969,7 +9693,7 @@ static char *fast_itoa(char *output, int64_t value) noexcept { char buffer[20]; uint64_t value_positive; // In general, negating a signed integer is unsafe. - if(value < 0) { + if (value < 0) { *output++ = '-'; // Doing value_positive = -value; while avoiding // undefined behavior warnings. @@ -8988,7 +9712,7 @@ static char *fast_itoa(char *output, int64_t value) noexcept { // A faster approach is possible if we expect large integers: // unroll the loop (work in 100s, 1000s) and use some kind of // memoization. - while(value_positive >= 10) { + while (value_positive >= 10) { *write_pointer-- = char('0' + (value_positive % 10)); value_positive /= 10; } @@ -9014,7 +9738,7 @@ static char *fast_itoa(char *output, uint64_t value) noexcept { // A faster approach is possible if we expect large integers: // unroll the loop (work in 100s, 1000s) and use some kind of // memoization. - while(value >= 10) { + while (value >= 10) { *write_pointer-- = char('0' + (value % 10)); value /= 10; }; @@ -9024,7 +9748,6 @@ static char *fast_itoa(char *output, uint64_t value) noexcept { return output + len; } - } // anonymous namespace namespace internal { @@ -9032,194 +9755,209 @@ namespace internal { * Minifier/formatter code. **/ -template +template simdjson_inline void base_formatter::number(uint64_t x) { char number_buffer[24]; char *newp = fast_itoa(number_buffer, x); - buffer.insert(buffer.end(), number_buffer, newp); + chars(number_buffer, newp); } -template +template simdjson_inline void base_formatter::number(int64_t x) { char number_buffer[24]; char *newp = fast_itoa(number_buffer, x); - buffer.insert(buffer.end(), number_buffer, newp); + chars(number_buffer, newp); } -template +template simdjson_inline void base_formatter::number(double x) { char number_buffer[24]; // Currently, passing the nullptr to the second argument is // safe because our implementation does not check the second // argument. char *newp = internal::to_chars(number_buffer, nullptr, x); - buffer.insert(buffer.end(), number_buffer, newp); + chars(number_buffer, newp); } -template -simdjson_inline void base_formatter::start_array() { one_char('['); } - +template +simdjson_inline void base_formatter::start_array() { + one_char('['); +} -template -simdjson_inline void base_formatter::end_array() { one_char(']'); } +template +simdjson_inline void base_formatter::end_array() { + one_char(']'); +} -template -simdjson_inline void base_formatter::start_object() { one_char('{'); } +template +simdjson_inline void base_formatter::start_object() { + one_char('{'); +} -template -simdjson_inline void base_formatter::end_object() { one_char('}'); } +template +simdjson_inline void base_formatter::end_object() { + one_char('}'); +} -template -simdjson_inline void base_formatter::comma() { one_char(','); } +template +simdjson_inline void base_formatter::comma() { + one_char(','); +} -template +template simdjson_inline void base_formatter::true_atom() { - const char * s = "true"; - buffer.insert(buffer.end(), s, s + 4); + const char *s = "true"; + chars(s, s + 4); } -template +template simdjson_inline void base_formatter::false_atom() { - const char * s = "false"; - buffer.insert(buffer.end(), s, s + 5); + const char *s = "false"; + chars(s, s + 5); } -template +template simdjson_inline void base_formatter::null_atom() { - const char * s = "null"; - buffer.insert(buffer.end(), s, s + 4); + const char *s = "null"; + chars(s, s + 4); +} + +template +simdjson_inline void base_formatter::one_char(char c) { + buffer.push_back(c); } -template -simdjson_inline void base_formatter::one_char(char c) { buffer.push_back(c); } +template +simdjson_inline void base_formatter::chars(const char *begin, + const char *end) { + buffer.append(begin, end); +} -template -simdjson_inline void base_formatter::key(std::string_view unescaped) { +template +simdjson_inline void +base_formatter::key(std::string_view unescaped) { string(unescaped); one_char(':'); } -template -simdjson_inline void base_formatter::string(std::string_view unescaped) { +template +simdjson_inline void +base_formatter::string(std::string_view unescaped) { one_char('\"'); size_t i = 0; - // Fast path for the case where we have no control character, no ", and no backslash. - // This should include most keys. - // - // We would like to use 'bool' but some compilers take offense to bitwise operation - // with bool types. - constexpr static char needs_escaping[] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 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, 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, 1, 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, 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, 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, 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, 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, 0, 0, 0}; - for(;i + 8 <= unescaped.length(); i += 8) { + // Fast path for the case where we have no control character, no ", and no + // backslash. This should include most keys. + // + // We would like to use 'bool' but some compilers take offense to bitwise + // operation with bool types. + constexpr static char needs_escaping[] = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 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, 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, 1, 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, 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, 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, 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, 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, 0, 0, 0}; + for (; i + 8 <= unescaped.length(); i += 8) { // Poor's man vectorization. This could get much faster if we used SIMD. // - // It is not the case that replacing '|' with '||' would be neutral performance-wise. - if(needs_escaping[uint8_t(unescaped[i])] | needs_escaping[uint8_t(unescaped[i+1])] - | needs_escaping[uint8_t(unescaped[i+2])] | needs_escaping[uint8_t(unescaped[i+3])] - | needs_escaping[uint8_t(unescaped[i+4])] | needs_escaping[uint8_t(unescaped[i+5])] - | needs_escaping[uint8_t(unescaped[i+6])] | needs_escaping[uint8_t(unescaped[i+7])] - ) { break; } - } - for(;i < unescaped.length(); i++) { - if(needs_escaping[uint8_t(unescaped[i])]) { break; } - } - // The following is also possible and omits a 256-byte table, but it is slower: - // for (; (i < unescaped.length()) && (uint8_t(unescaped[i]) > 0x1F) + // It is not the case that replacing '|' with '||' would be neutral + // performance-wise. + if (needs_escaping[uint8_t(unescaped[i])] | + needs_escaping[uint8_t(unescaped[i + 1])] | + needs_escaping[uint8_t(unescaped[i + 2])] | + needs_escaping[uint8_t(unescaped[i + 3])] | + needs_escaping[uint8_t(unescaped[i + 4])] | + needs_escaping[uint8_t(unescaped[i + 5])] | + needs_escaping[uint8_t(unescaped[i + 6])] | + needs_escaping[uint8_t(unescaped[i + 7])]) { + break; + } + } + for (; i < unescaped.length(); i++) { + if (needs_escaping[uint8_t(unescaped[i])]) { + break; + } + } + // The following is also possible and omits a 256-byte table, but it is + // slower: for (; (i < unescaped.length()) && (uint8_t(unescaped[i]) > 0x1F) // && (unescaped[i] != '\"') && (unescaped[i] != '\\'); i++) {} // At least for long strings, the following should be fast. We could // do better by integrating the checks and the insertion. - buffer.insert(buffer.end(), unescaped.data(), unescaped.data() + i); + chars(unescaped.data(), unescaped.data() + i); // We caught a control character if we enter this loop (slow). // Note that we are do not restart from the beginning, but rather we continue // from the point where we encountered something that requires escaping. for (; i < unescaped.length(); i++) { switch (unescaped[i]) { - case '\"': - { - const char * s = "\\\""; - buffer.insert(buffer.end(), s, s + 2); - } - break; - case '\\': - { - const char * s = "\\\\"; - buffer.insert(buffer.end(), s, s + 2); - } - break; + case '\"': { + const char *s = "\\\""; + chars(s, s + 2); + } break; + case '\\': { + const char *s = "\\\\"; + chars(s, s + 2); + } break; default: if (uint8_t(unescaped[i]) <= 0x1F) { // If packed, this uses 8 * 32 bytes. // Note that we expect most compilers to embed this code in the data // section. constexpr static escape_sequence escaped[32] = { - {6, "\\u0000"}, {6, "\\u0001"}, {6, "\\u0002"}, {6, "\\u0003"}, - {6, "\\u0004"}, {6, "\\u0005"}, {6, "\\u0006"}, {6, "\\u0007"}, - {2, "\\b"}, {2, "\\t"}, {2, "\\n"}, {6, "\\u000b"}, - {2, "\\f"}, {2, "\\r"}, {6, "\\u000e"}, {6, "\\u000f"}, - {6, "\\u0010"}, {6, "\\u0011"}, {6, "\\u0012"}, {6, "\\u0013"}, - {6, "\\u0014"}, {6, "\\u0015"}, {6, "\\u0016"}, {6, "\\u0017"}, - {6, "\\u0018"}, {6, "\\u0019"}, {6, "\\u001a"}, {6, "\\u001b"}, - {6, "\\u001c"}, {6, "\\u001d"}, {6, "\\u001e"}, {6, "\\u001f"}}; + {6, "\\u0000"}, {6, "\\u0001"}, {6, "\\u0002"}, {6, "\\u0003"}, + {6, "\\u0004"}, {6, "\\u0005"}, {6, "\\u0006"}, {6, "\\u0007"}, + {2, "\\b"}, {2, "\\t"}, {2, "\\n"}, {6, "\\u000b"}, + {2, "\\f"}, {2, "\\r"}, {6, "\\u000e"}, {6, "\\u000f"}, + {6, "\\u0010"}, {6, "\\u0011"}, {6, "\\u0012"}, {6, "\\u0013"}, + {6, "\\u0014"}, {6, "\\u0015"}, {6, "\\u0016"}, {6, "\\u0017"}, + {6, "\\u0018"}, {6, "\\u0019"}, {6, "\\u001a"}, {6, "\\u001b"}, + {6, "\\u001c"}, {6, "\\u001d"}, {6, "\\u001e"}, {6, "\\u001f"}}; auto u = escaped[uint8_t(unescaped[i])]; - buffer.insert(buffer.end(), u.string, u.string + u.length); + chars(u.string, u.string + u.length); } else { one_char(unescaped[i]); } } // switch - } // for + } // for one_char('\"'); } - -template -inline void base_formatter::clear() { +template inline void base_formatter::clear() { buffer.clear(); } -template +template simdjson_inline std::string_view base_formatter::str() const { - return std::string_view(buffer.data(), buffer.size()); + return buffer.str(); } -simdjson_inline void mini_formatter::print_newline() { - return; -} +simdjson_inline void mini_formatter::print_newline() { return; } simdjson_inline void mini_formatter::print_indents(size_t depth) { - (void)depth; - return; + (void)depth; + return; } -simdjson_inline void mini_formatter::print_space() { - return; -} +simdjson_inline void mini_formatter::print_space() { return; } -simdjson_inline void pretty_formatter::print_newline() { - one_char('\n'); -} +simdjson_inline void pretty_formatter::print_newline() { one_char('\n'); } simdjson_inline void pretty_formatter::print_indents(size_t depth) { - if(this->indent_step <= 0) { - return; - } - for(size_t i = 0; i < this->indent_step * depth; i++) { - one_char(' '); - } -} - -simdjson_inline void pretty_formatter::print_space() { + if (this->indent_step <= 0) { + return; + } + for (size_t i = 0; i < this->indent_step * depth; i++) { one_char(' '); + } } +simdjson_inline void pretty_formatter::print_space() { one_char(' '); } + /*** * String building code. **/ @@ -9397,7 +10135,8 @@ inline void string_builder::append(simdjson::dom::array value) { } template -simdjson_inline void string_builder::append(simdjson::dom::key_value_pair kv) { +simdjson_inline void +string_builder::append(simdjson::dom::key_value_pair kv) { format.key(kv.key); append(kv.value); } @@ -9412,7 +10151,6 @@ simdjson_inline std::string_view string_builder::str() const { return format.str(); } - } // namespace internal } // namespace simdjson @@ -10623,6 +11361,12 @@ namespace { tmp = vpaddq_u8(tmp, tmp); return vgetq_lane_u16(vreinterpretq_u16_u8(tmp), 0); } + // Returns 4-bit out of each byte, alternating between the high 4 bits and low + // bits result it is 64 bit. + simdjson_inline uint64_t to_bitmask64() const { + return vget_lane_u64( + vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(*this), 4)), 0); + } simdjson_inline bool any() const { return vmaxvq_u32(vreinterpretq_u32_u8(*this)) != 0; } }; @@ -10699,7 +11443,7 @@ namespace { // Bit-specific operations simdjson_inline simd8 any_bits_set(simd8 bits) const { return vtstq_u8(*this, bits); } - simdjson_inline bool any_bits_set_anywhere() const { return this->max_val() != 0; } + simdjson_inline bool any_bits_set_anywhere() const { return vmaxvq_u32(vreinterpretq_u32_u8(*this)) != 0; } simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { return (*this & bits).any_bits_set_anywhere(); } template simdjson_inline simd8 shr() const { return vshrq_n_u8(*this, N); } @@ -10712,7 +11456,12 @@ namespace { return lookup_table.apply_lookup_16_to(*this); } - + // Returns 4-bit out of each byte, alternating between the high 4 bits and low + // bits result it is 64 bit. + simdjson_inline uint64_t to_bitmask64() const { + return vget_lane_u64( + vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(*this), 4)), 0); + } // Copies to 'output" all bytes corresponding to a 0 in the mask (interpreted as a bitset). // Passing a 0 value for mask would be equivalent to writing out every byte to output. // Only the first 16 - count_ones(mask) bytes of the result are significant but 16 bytes @@ -11006,7 +11755,7 @@ using namespace simd; struct backslash_and_quote { public: static constexpr uint32_t BYTES_PROCESSED = 32; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); + simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; } simdjson_inline bool has_backslash() { return bs_bits != 0; } @@ -11035,6 +11784,32 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin }; } +struct escaping { + static constexpr uint32_t BYTES_PROCESSED = 16; + simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst); + + simdjson_inline bool has_escape() { return escape_bits != 0; } + simdjson_inline int escape_index() { return trailing_zeroes(escape_bits) / 4; } + + uint64_t escape_bits; +}; // struct escaping + + + +simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) { + static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes"); + simd8 v(src); + v.store(dst); + simd8 is_quote = (v == '"'); + simd8 is_backslash = (v == '\\'); + simd8 is_control = (v < 32); + return { + (is_backslash | is_quote | is_control).to_bitmask64() + }; +} + + + } // unnamed namespace } // namespace arm64 } // namespace simdjson @@ -11454,12 +12229,17 @@ struct implementation_simdjson_result_base { * * @param value The variable to assign the value to. May not be set if there is an error. */ - simdjson_inline error_code get(T &value) && noexcept; + simdjson_warn_unused simdjson_inline error_code get(T &value) && noexcept; /** * The error. */ - simdjson_inline error_code error() const noexcept; + simdjson_warn_unused simdjson_inline error_code error() const noexcept; + + /** + * Whether there is a value. + */ + simdjson_warn_unused simdjson_inline bool has_value() const noexcept; #if SIMDJSON_EXCEPTIONS @@ -11468,6 +12248,16 @@ struct implementation_simdjson_result_base { * * @throw simdjson_error if there was an error. */ + simdjson_inline T& operator*() & noexcept(false); + simdjson_inline T&& operator*() && noexcept(false); + /** + * Arrow operator to access members of the contained value. + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T* operator->() noexcept(false); + simdjson_inline const T* operator->() const noexcept(false); + simdjson_inline T& value() & noexcept(false); /** @@ -11509,6 +12299,7 @@ struct implementation_simdjson_result_base { * the error() method returns a value that evaluates to false. */ simdjson_inline T&& value_unsafe() && noexcept; + protected: /** users should never directly access first and second. **/ T first{}; /** Users should never directly access 'first'. **/ @@ -11885,7 +12676,7 @@ simdjson_inline bool is_digit(const uint8_t c) { return static_cast(c - '0') <= 9; } -simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const uint8_t *const src, const uint8_t *&p, uint64_t &i, int64_t &exponent) { +simdjson_warn_unused simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const uint8_t *const src, const uint8_t *&p, uint64_t &i, int64_t &exponent) { // we continue with the fiction that we have an integer. If the // floating point number is representable as x * 10^z for some integer // z that fits in 53 bits, then we will be able to convert back the @@ -11913,7 +12704,7 @@ simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const u return SUCCESS; } -simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const src, const uint8_t *&p, int64_t &exponent) { +simdjson_warn_unused simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const src, const uint8_t *&p, int64_t &exponent) { // Exp Sign: -123.456e[-]78 bool neg_exp = ('-' == *p); if (neg_exp || '+' == *p) { p++; } // Skip + as well @@ -12002,7 +12793,7 @@ static error_code slow_float_parsing(simdjson_unused const uint8_t * src, double /** @private */ template -simdjson_inline error_code write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer) { +simdjson_warn_unused simdjson_inline error_code write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer) { // If we frequently had to deal with long strings of digits, // we could extend our code by using a 128-bit integer instead // of a 64-bit integer. However, this is uncommon in practice. @@ -12065,13 +12856,13 @@ simdjson_inline error_code write_float(const uint8_t *const src, bool negative, // // Our objective is accurate parsing (ULP of 0) at high speed. template -simdjson_inline error_code parse_number(const uint8_t *const src, W &writer); +simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const src, W &writer); // for performance analysis, it is sometimes useful to skip parsing #ifdef SIMDJSON_SKIPNUMBERPARSING template -simdjson_inline error_code parse_number(const uint8_t *const, W &writer) { +simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const, W &writer) { writer.append_s64(0); // always write zero return SUCCESS; // always succeeds } @@ -12097,7 +12888,7 @@ simdjson_unused simdjson_inline simdjson_result get_number_type(con // // Our objective is accurate parsing (ULP of 0) at high speed. template -simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { +simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { // // Check for minus sign // @@ -12881,12 +13672,41 @@ simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_b } template -simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { +simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { return this->second; } + +template +simdjson_warn_unused simdjson_inline bool implementation_simdjson_result_base::has_value() const noexcept { + return this->error() == SUCCESS; +} + #if SIMDJSON_EXCEPTIONS +template +simdjson_inline T& implementation_simdjson_result_base::operator*() & noexcept(false) { + return this->value(); +} + +template +simdjson_inline T&& implementation_simdjson_result_base::operator*() && noexcept(false) { + return std::forward>(*this).value(); +} + +template +simdjson_inline T* implementation_simdjson_result_base::operator->() noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + + +template +simdjson_inline const T* implementation_simdjson_result_base::operator->() const noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + template simdjson_inline T& implementation_simdjson_result_base::value() & noexcept(false) { if (error()) { throw simdjson_error(error()); } @@ -13055,7 +13875,7 @@ namespace { struct backslash_and_quote { public: static constexpr uint32_t BYTES_PROCESSED = 1; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); + simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); simdjson_inline bool has_quote_first() { return c == '"'; } simdjson_inline bool has_backslash() { return c == '\\'; } @@ -13071,6 +13891,24 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin return { src[0] }; } + +struct escaping { + static constexpr uint32_t BYTES_PROCESSED = 1; + simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst); + + simdjson_inline bool has_escape() { return escape_bits; } + simdjson_inline int escape_index() { return 0; } + + bool escape_bits; +}; // struct escaping + + + +simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) { + dst[0] = src[0]; + return { (src[0] == '\\') || (src[0] == '"') || (src[0] < 32) }; +} + } // unnamed namespace } // namespace fallback } // namespace simdjson @@ -13577,12 +14415,17 @@ struct implementation_simdjson_result_base { * * @param value The variable to assign the value to. May not be set if there is an error. */ - simdjson_inline error_code get(T &value) && noexcept; + simdjson_warn_unused simdjson_inline error_code get(T &value) && noexcept; /** * The error. */ - simdjson_inline error_code error() const noexcept; + simdjson_warn_unused simdjson_inline error_code error() const noexcept; + + /** + * Whether there is a value. + */ + simdjson_warn_unused simdjson_inline bool has_value() const noexcept; #if SIMDJSON_EXCEPTIONS @@ -13591,6 +14434,16 @@ struct implementation_simdjson_result_base { * * @throw simdjson_error if there was an error. */ + simdjson_inline T& operator*() & noexcept(false); + simdjson_inline T&& operator*() && noexcept(false); + /** + * Arrow operator to access members of the contained value. + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T* operator->() noexcept(false); + simdjson_inline const T* operator->() const noexcept(false); + simdjson_inline T& value() & noexcept(false); /** @@ -13632,6 +14485,7 @@ struct implementation_simdjson_result_base { * the error() method returns a value that evaluates to false. */ simdjson_inline T&& value_unsafe() && noexcept; + protected: /** users should never directly access first and second. **/ T first{}; /** Users should never directly access 'first'. **/ @@ -14008,7 +14862,7 @@ simdjson_inline bool is_digit(const uint8_t c) { return static_cast(c - '0') <= 9; } -simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const uint8_t *const src, const uint8_t *&p, uint64_t &i, int64_t &exponent) { +simdjson_warn_unused simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const uint8_t *const src, const uint8_t *&p, uint64_t &i, int64_t &exponent) { // we continue with the fiction that we have an integer. If the // floating point number is representable as x * 10^z for some integer // z that fits in 53 bits, then we will be able to convert back the @@ -14036,7 +14890,7 @@ simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const u return SUCCESS; } -simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const src, const uint8_t *&p, int64_t &exponent) { +simdjson_warn_unused simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const src, const uint8_t *&p, int64_t &exponent) { // Exp Sign: -123.456e[-]78 bool neg_exp = ('-' == *p); if (neg_exp || '+' == *p) { p++; } // Skip + as well @@ -14125,7 +14979,7 @@ static error_code slow_float_parsing(simdjson_unused const uint8_t * src, double /** @private */ template -simdjson_inline error_code write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer) { +simdjson_warn_unused simdjson_inline error_code write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer) { // If we frequently had to deal with long strings of digits, // we could extend our code by using a 128-bit integer instead // of a 64-bit integer. However, this is uncommon in practice. @@ -14188,13 +15042,13 @@ simdjson_inline error_code write_float(const uint8_t *const src, bool negative, // // Our objective is accurate parsing (ULP of 0) at high speed. template -simdjson_inline error_code parse_number(const uint8_t *const src, W &writer); +simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const src, W &writer); // for performance analysis, it is sometimes useful to skip parsing #ifdef SIMDJSON_SKIPNUMBERPARSING template -simdjson_inline error_code parse_number(const uint8_t *const, W &writer) { +simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const, W &writer) { writer.append_s64(0); // always write zero return SUCCESS; // always succeeds } @@ -14220,7 +15074,7 @@ simdjson_unused simdjson_inline simdjson_result get_number_type(con // // Our objective is accurate parsing (ULP of 0) at high speed. template -simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { +simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { // // Check for minus sign // @@ -15004,12 +15858,41 @@ simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_b } template -simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { +simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { return this->second; } + +template +simdjson_warn_unused simdjson_inline bool implementation_simdjson_result_base::has_value() const noexcept { + return this->error() == SUCCESS; +} + #if SIMDJSON_EXCEPTIONS +template +simdjson_inline T& implementation_simdjson_result_base::operator*() & noexcept(false) { + return this->value(); +} + +template +simdjson_inline T&& implementation_simdjson_result_base::operator*() && noexcept(false) { + return std::forward>(*this).value(); +} + +template +simdjson_inline T* implementation_simdjson_result_base::operator->() noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + + +template +simdjson_inline const T* implementation_simdjson_result_base::operator->() const noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + template simdjson_inline T& implementation_simdjson_result_base::value() & noexcept(false) { if (error()) { throw simdjson_error(error()); } @@ -15751,7 +16634,7 @@ using namespace simd; struct backslash_and_quote { public: static constexpr uint32_t BYTES_PROCESSED = 32; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); + simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; } simdjson_inline bool has_backslash() { return ((quote_bits - 1) & bs_bits) != 0; } @@ -15775,6 +16658,31 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin }; } + +struct escaping { + static constexpr uint32_t BYTES_PROCESSED = 32; + simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst); + + simdjson_inline bool has_escape() { return escape_bits != 0; } + simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); } + + uint64_t escape_bits; +}; // struct escaping + + + +simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) { + static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes"); + simd8 v(src); + v.store(dst); + simd8 is_quote = (v == '"'); + simd8 is_backslash = (v == '\\'); + simd8 is_control = (v < 32); + return { + uint64_t((is_backslash | is_quote | is_control).to_bitmask()) + }; +} + } // unnamed namespace } // namespace haswell } // namespace simdjson @@ -16192,12 +17100,17 @@ struct implementation_simdjson_result_base { * * @param value The variable to assign the value to. May not be set if there is an error. */ - simdjson_inline error_code get(T &value) && noexcept; + simdjson_warn_unused simdjson_inline error_code get(T &value) && noexcept; /** * The error. */ - simdjson_inline error_code error() const noexcept; + simdjson_warn_unused simdjson_inline error_code error() const noexcept; + + /** + * Whether there is a value. + */ + simdjson_warn_unused simdjson_inline bool has_value() const noexcept; #if SIMDJSON_EXCEPTIONS @@ -16206,6 +17119,16 @@ struct implementation_simdjson_result_base { * * @throw simdjson_error if there was an error. */ + simdjson_inline T& operator*() & noexcept(false); + simdjson_inline T&& operator*() && noexcept(false); + /** + * Arrow operator to access members of the contained value. + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T* operator->() noexcept(false); + simdjson_inline const T* operator->() const noexcept(false); + simdjson_inline T& value() & noexcept(false); /** @@ -16247,6 +17170,7 @@ struct implementation_simdjson_result_base { * the error() method returns a value that evaluates to false. */ simdjson_inline T&& value_unsafe() && noexcept; + protected: /** users should never directly access first and second. **/ T first{}; /** Users should never directly access 'first'. **/ @@ -16623,7 +17547,7 @@ simdjson_inline bool is_digit(const uint8_t c) { return static_cast(c - '0') <= 9; } -simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const uint8_t *const src, const uint8_t *&p, uint64_t &i, int64_t &exponent) { +simdjson_warn_unused simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const uint8_t *const src, const uint8_t *&p, uint64_t &i, int64_t &exponent) { // we continue with the fiction that we have an integer. If the // floating point number is representable as x * 10^z for some integer // z that fits in 53 bits, then we will be able to convert back the @@ -16651,7 +17575,7 @@ simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const u return SUCCESS; } -simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const src, const uint8_t *&p, int64_t &exponent) { +simdjson_warn_unused simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const src, const uint8_t *&p, int64_t &exponent) { // Exp Sign: -123.456e[-]78 bool neg_exp = ('-' == *p); if (neg_exp || '+' == *p) { p++; } // Skip + as well @@ -16740,7 +17664,7 @@ static error_code slow_float_parsing(simdjson_unused const uint8_t * src, double /** @private */ template -simdjson_inline error_code write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer) { +simdjson_warn_unused simdjson_inline error_code write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer) { // If we frequently had to deal with long strings of digits, // we could extend our code by using a 128-bit integer instead // of a 64-bit integer. However, this is uncommon in practice. @@ -16803,13 +17727,13 @@ simdjson_inline error_code write_float(const uint8_t *const src, bool negative, // // Our objective is accurate parsing (ULP of 0) at high speed. template -simdjson_inline error_code parse_number(const uint8_t *const src, W &writer); +simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const src, W &writer); // for performance analysis, it is sometimes useful to skip parsing #ifdef SIMDJSON_SKIPNUMBERPARSING template -simdjson_inline error_code parse_number(const uint8_t *const, W &writer) { +simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const, W &writer) { writer.append_s64(0); // always write zero return SUCCESS; // always succeeds } @@ -16835,7 +17759,7 @@ simdjson_unused simdjson_inline simdjson_result get_number_type(con // // Our objective is accurate parsing (ULP of 0) at high speed. template -simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { +simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { // // Check for minus sign // @@ -17619,12 +18543,41 @@ simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_b } template -simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { +simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { return this->second; } + +template +simdjson_warn_unused simdjson_inline bool implementation_simdjson_result_base::has_value() const noexcept { + return this->error() == SUCCESS; +} + #if SIMDJSON_EXCEPTIONS +template +simdjson_inline T& implementation_simdjson_result_base::operator*() & noexcept(false) { + return this->value(); +} + +template +simdjson_inline T&& implementation_simdjson_result_base::operator*() && noexcept(false) { + return std::forward>(*this).value(); +} + +template +simdjson_inline T* implementation_simdjson_result_base::operator->() noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + + +template +simdjson_inline const T* implementation_simdjson_result_base::operator->() const noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + template simdjson_inline T& implementation_simdjson_result_base::value() & noexcept(false) { if (error()) { throw simdjson_error(error()); } @@ -17984,7 +18937,6 @@ namespace simd { friend simdjson_really_inline uint64_t operator==(const simd8 lhs, const simd8 rhs) { return _mm512_cmpeq_epi8_mask(lhs, rhs); } - static const int SIZE = sizeof(base::value); template @@ -18303,7 +19255,7 @@ using namespace simd; struct backslash_and_quote { public: static constexpr uint32_t BYTES_PROCESSED = 64; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); + simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; } simdjson_inline bool has_backslash() { return ((quote_bits - 1) & bs_bits) != 0; } @@ -18327,6 +19279,35 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin }; } + + +struct escaping { + static constexpr uint32_t BYTES_PROCESSED = 64; + simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst); + + simdjson_inline bool has_escape() { return escape_bits != 0; } + simdjson_inline int escape_index() { return trailing_zeroes(uint64_t(escape_bits)); } + + __mmask64 escape_bits; +}; // struct escaping + + + +simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) { + static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes"); + simd8 v(src); + v.store(dst); + __mmask64 is_quote = _mm512_cmpeq_epi8_mask(v, _mm512_set1_epi8('"')); + __mmask64 is_backslash = _mm512_cmpeq_epi8_mask(v, _mm512_set1_epi8('\\')); + __mmask64 is_control = _mm512_cmplt_epi8_mask(v, _mm512_set1_epi8(32)); + return { + (is_backslash | is_quote | is_control) + }; +} + + + + } // unnamed namespace } // namespace icelake } // namespace simdjson @@ -18804,12 +19785,17 @@ struct implementation_simdjson_result_base { * * @param value The variable to assign the value to. May not be set if there is an error. */ - simdjson_inline error_code get(T &value) && noexcept; + simdjson_warn_unused simdjson_inline error_code get(T &value) && noexcept; /** * The error. */ - simdjson_inline error_code error() const noexcept; + simdjson_warn_unused simdjson_inline error_code error() const noexcept; + + /** + * Whether there is a value. + */ + simdjson_warn_unused simdjson_inline bool has_value() const noexcept; #if SIMDJSON_EXCEPTIONS @@ -18818,6 +19804,16 @@ struct implementation_simdjson_result_base { * * @throw simdjson_error if there was an error. */ + simdjson_inline T& operator*() & noexcept(false); + simdjson_inline T&& operator*() && noexcept(false); + /** + * Arrow operator to access members of the contained value. + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T* operator->() noexcept(false); + simdjson_inline const T* operator->() const noexcept(false); + simdjson_inline T& value() & noexcept(false); /** @@ -18859,6 +19855,7 @@ struct implementation_simdjson_result_base { * the error() method returns a value that evaluates to false. */ simdjson_inline T&& value_unsafe() && noexcept; + protected: /** users should never directly access first and second. **/ T first{}; /** Users should never directly access 'first'. **/ @@ -19235,7 +20232,7 @@ simdjson_inline bool is_digit(const uint8_t c) { return static_cast(c - '0') <= 9; } -simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const uint8_t *const src, const uint8_t *&p, uint64_t &i, int64_t &exponent) { +simdjson_warn_unused simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const uint8_t *const src, const uint8_t *&p, uint64_t &i, int64_t &exponent) { // we continue with the fiction that we have an integer. If the // floating point number is representable as x * 10^z for some integer // z that fits in 53 bits, then we will be able to convert back the @@ -19263,7 +20260,7 @@ simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const u return SUCCESS; } -simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const src, const uint8_t *&p, int64_t &exponent) { +simdjson_warn_unused simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const src, const uint8_t *&p, int64_t &exponent) { // Exp Sign: -123.456e[-]78 bool neg_exp = ('-' == *p); if (neg_exp || '+' == *p) { p++; } // Skip + as well @@ -19352,7 +20349,7 @@ static error_code slow_float_parsing(simdjson_unused const uint8_t * src, double /** @private */ template -simdjson_inline error_code write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer) { +simdjson_warn_unused simdjson_inline error_code write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer) { // If we frequently had to deal with long strings of digits, // we could extend our code by using a 128-bit integer instead // of a 64-bit integer. However, this is uncommon in practice. @@ -19415,13 +20412,13 @@ simdjson_inline error_code write_float(const uint8_t *const src, bool negative, // // Our objective is accurate parsing (ULP of 0) at high speed. template -simdjson_inline error_code parse_number(const uint8_t *const src, W &writer); +simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const src, W &writer); // for performance analysis, it is sometimes useful to skip parsing #ifdef SIMDJSON_SKIPNUMBERPARSING template -simdjson_inline error_code parse_number(const uint8_t *const, W &writer) { +simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const, W &writer) { writer.append_s64(0); // always write zero return SUCCESS; // always succeeds } @@ -19447,7 +20444,7 @@ simdjson_unused simdjson_inline simdjson_result get_number_type(con // // Our objective is accurate parsing (ULP of 0) at high speed. template -simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { +simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { // // Check for minus sign // @@ -20231,12 +21228,41 @@ simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_b } template -simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { +simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { return this->second; } + +template +simdjson_warn_unused simdjson_inline bool implementation_simdjson_result_base::has_value() const noexcept { + return this->error() == SUCCESS; +} + #if SIMDJSON_EXCEPTIONS +template +simdjson_inline T& implementation_simdjson_result_base::operator*() & noexcept(false) { + return this->value(); +} + +template +simdjson_inline T&& implementation_simdjson_result_base::operator*() && noexcept(false) { + return std::forward>(*this).value(); +} + +template +simdjson_inline T* implementation_simdjson_result_base::operator->() noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + + +template +simdjson_inline const T* implementation_simdjson_result_base::operator->() const noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + template simdjson_inline T& implementation_simdjson_result_base::value() & noexcept(false) { if (error()) { throw simdjson_error(error()); } @@ -21073,7 +22099,7 @@ using namespace simd; struct backslash_and_quote { public: static constexpr uint32_t BYTES_PROCESSED = 32; - simdjson_inline static backslash_and_quote + simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); simdjson_inline bool has_quote_first() { @@ -21114,6 +22140,32 @@ backslash_and_quote::copy_and_find(const uint8_t *src, uint8_t *dst) { }; } + +struct escaping { + static constexpr uint32_t BYTES_PROCESSED = 16; + simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst); + + simdjson_inline bool has_escape() { return escape_bits != 0; } + simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); } + + uint64_t escape_bits; +}; // struct escaping + + + +simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) { + static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes"); + simd8 v(src); + v.store(dst); + simd8 is_quote = (v == '"'); + simd8 is_backslash = (v == '\\'); + simd8 is_control = (v < 32); + return { + // We store it as a 64-bit bitmask even though we only need 16 bits. + uint64_t((is_backslash | is_quote | is_control).to_bitmask()) + }; +} + } // unnamed namespace } // namespace ppc64 } // namespace simdjson @@ -21533,12 +22585,17 @@ struct implementation_simdjson_result_base { * * @param value The variable to assign the value to. May not be set if there is an error. */ - simdjson_inline error_code get(T &value) && noexcept; + simdjson_warn_unused simdjson_inline error_code get(T &value) && noexcept; /** * The error. */ - simdjson_inline error_code error() const noexcept; + simdjson_warn_unused simdjson_inline error_code error() const noexcept; + + /** + * Whether there is a value. + */ + simdjson_warn_unused simdjson_inline bool has_value() const noexcept; #if SIMDJSON_EXCEPTIONS @@ -21547,6 +22604,16 @@ struct implementation_simdjson_result_base { * * @throw simdjson_error if there was an error. */ + simdjson_inline T& operator*() & noexcept(false); + simdjson_inline T&& operator*() && noexcept(false); + /** + * Arrow operator to access members of the contained value. + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T* operator->() noexcept(false); + simdjson_inline const T* operator->() const noexcept(false); + simdjson_inline T& value() & noexcept(false); /** @@ -21588,6 +22655,7 @@ struct implementation_simdjson_result_base { * the error() method returns a value that evaluates to false. */ simdjson_inline T&& value_unsafe() && noexcept; + protected: /** users should never directly access first and second. **/ T first{}; /** Users should never directly access 'first'. **/ @@ -21964,7 +23032,7 @@ simdjson_inline bool is_digit(const uint8_t c) { return static_cast(c - '0') <= 9; } -simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const uint8_t *const src, const uint8_t *&p, uint64_t &i, int64_t &exponent) { +simdjson_warn_unused simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const uint8_t *const src, const uint8_t *&p, uint64_t &i, int64_t &exponent) { // we continue with the fiction that we have an integer. If the // floating point number is representable as x * 10^z for some integer // z that fits in 53 bits, then we will be able to convert back the @@ -21992,7 +23060,7 @@ simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const u return SUCCESS; } -simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const src, const uint8_t *&p, int64_t &exponent) { +simdjson_warn_unused simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const src, const uint8_t *&p, int64_t &exponent) { // Exp Sign: -123.456e[-]78 bool neg_exp = ('-' == *p); if (neg_exp || '+' == *p) { p++; } // Skip + as well @@ -22081,7 +23149,7 @@ static error_code slow_float_parsing(simdjson_unused const uint8_t * src, double /** @private */ template -simdjson_inline error_code write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer) { +simdjson_warn_unused simdjson_inline error_code write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer) { // If we frequently had to deal with long strings of digits, // we could extend our code by using a 128-bit integer instead // of a 64-bit integer. However, this is uncommon in practice. @@ -22144,13 +23212,13 @@ simdjson_inline error_code write_float(const uint8_t *const src, bool negative, // // Our objective is accurate parsing (ULP of 0) at high speed. template -simdjson_inline error_code parse_number(const uint8_t *const src, W &writer); +simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const src, W &writer); // for performance analysis, it is sometimes useful to skip parsing #ifdef SIMDJSON_SKIPNUMBERPARSING template -simdjson_inline error_code parse_number(const uint8_t *const, W &writer) { +simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const, W &writer) { writer.append_s64(0); // always write zero return SUCCESS; // always succeeds } @@ -22176,7 +23244,7 @@ simdjson_unused simdjson_inline simdjson_result get_number_type(con // // Our objective is accurate parsing (ULP of 0) at high speed. template -simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { +simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { // // Check for minus sign // @@ -22960,12 +24028,41 @@ simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_b } template -simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { +simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { return this->second; } + +template +simdjson_warn_unused simdjson_inline bool implementation_simdjson_result_base::has_value() const noexcept { + return this->error() == SUCCESS; +} + #if SIMDJSON_EXCEPTIONS +template +simdjson_inline T& implementation_simdjson_result_base::operator*() & noexcept(false) { + return this->value(); +} + +template +simdjson_inline T&& implementation_simdjson_result_base::operator*() && noexcept(false) { + return std::forward>(*this).value(); +} + +template +simdjson_inline T* implementation_simdjson_result_base::operator->() noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + + +template +simdjson_inline const T* implementation_simdjson_result_base::operator->() const noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + template simdjson_inline T& implementation_simdjson_result_base::value() & noexcept(false) { if (error()) { throw simdjson_error(error()); } @@ -24136,7 +25233,7 @@ using namespace simd; struct backslash_and_quote { public: static constexpr uint32_t BYTES_PROCESSED = 32; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); + simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; } simdjson_inline bool has_backslash() { return bs_bits != 0; } @@ -24162,6 +25259,31 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin }; } + +struct escaping { + static constexpr uint32_t BYTES_PROCESSED = 16; + simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst); + + simdjson_inline bool has_escape() { return escape_bits != 0; } + simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); } + + uint64_t escape_bits; +}; // struct escaping + + + +simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) { + static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes"); + simd8 v(src); + v.store(dst); + simd8 is_quote = (v == '"'); + simd8 is_backslash = (v == '\\'); + simd8 is_control = (v < 32); + return { + uint64_t((is_backslash | is_quote | is_control).to_bitmask()) + }; +} + } // unnamed namespace } // namespace westmere } // namespace simdjson @@ -24579,12 +25701,17 @@ struct implementation_simdjson_result_base { * * @param value The variable to assign the value to. May not be set if there is an error. */ - simdjson_inline error_code get(T &value) && noexcept; + simdjson_warn_unused simdjson_inline error_code get(T &value) && noexcept; /** * The error. */ - simdjson_inline error_code error() const noexcept; + simdjson_warn_unused simdjson_inline error_code error() const noexcept; + + /** + * Whether there is a value. + */ + simdjson_warn_unused simdjson_inline bool has_value() const noexcept; #if SIMDJSON_EXCEPTIONS @@ -24593,6 +25720,16 @@ struct implementation_simdjson_result_base { * * @throw simdjson_error if there was an error. */ + simdjson_inline T& operator*() & noexcept(false); + simdjson_inline T&& operator*() && noexcept(false); + /** + * Arrow operator to access members of the contained value. + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T* operator->() noexcept(false); + simdjson_inline const T* operator->() const noexcept(false); + simdjson_inline T& value() & noexcept(false); /** @@ -24634,6 +25771,7 @@ struct implementation_simdjson_result_base { * the error() method returns a value that evaluates to false. */ simdjson_inline T&& value_unsafe() && noexcept; + protected: /** users should never directly access first and second. **/ T first{}; /** Users should never directly access 'first'. **/ @@ -25010,7 +26148,7 @@ simdjson_inline bool is_digit(const uint8_t c) { return static_cast(c - '0') <= 9; } -simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const uint8_t *const src, const uint8_t *&p, uint64_t &i, int64_t &exponent) { +simdjson_warn_unused simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const uint8_t *const src, const uint8_t *&p, uint64_t &i, int64_t &exponent) { // we continue with the fiction that we have an integer. If the // floating point number is representable as x * 10^z for some integer // z that fits in 53 bits, then we will be able to convert back the @@ -25038,7 +26176,7 @@ simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const u return SUCCESS; } -simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const src, const uint8_t *&p, int64_t &exponent) { +simdjson_warn_unused simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const src, const uint8_t *&p, int64_t &exponent) { // Exp Sign: -123.456e[-]78 bool neg_exp = ('-' == *p); if (neg_exp || '+' == *p) { p++; } // Skip + as well @@ -25127,7 +26265,7 @@ static error_code slow_float_parsing(simdjson_unused const uint8_t * src, double /** @private */ template -simdjson_inline error_code write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer) { +simdjson_warn_unused simdjson_inline error_code write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer) { // If we frequently had to deal with long strings of digits, // we could extend our code by using a 128-bit integer instead // of a 64-bit integer. However, this is uncommon in practice. @@ -25190,13 +26328,13 @@ simdjson_inline error_code write_float(const uint8_t *const src, bool negative, // // Our objective is accurate parsing (ULP of 0) at high speed. template -simdjson_inline error_code parse_number(const uint8_t *const src, W &writer); +simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const src, W &writer); // for performance analysis, it is sometimes useful to skip parsing #ifdef SIMDJSON_SKIPNUMBERPARSING template -simdjson_inline error_code parse_number(const uint8_t *const, W &writer) { +simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const, W &writer) { writer.append_s64(0); // always write zero return SUCCESS; // always succeeds } @@ -25222,7 +26360,7 @@ simdjson_unused simdjson_inline simdjson_result get_number_type(con // // Our objective is accurate parsing (ULP of 0) at high speed. template -simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { +simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { // // Check for minus sign // @@ -26006,12 +27144,41 @@ simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_b } template -simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { +simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { return this->second; } + +template +simdjson_warn_unused simdjson_inline bool implementation_simdjson_result_base::has_value() const noexcept { + return this->error() == SUCCESS; +} + #if SIMDJSON_EXCEPTIONS +template +simdjson_inline T& implementation_simdjson_result_base::operator*() & noexcept(false) { + return this->value(); +} + +template +simdjson_inline T&& implementation_simdjson_result_base::operator*() && noexcept(false) { + return std::forward>(*this).value(); +} + +template +simdjson_inline T* implementation_simdjson_result_base::operator->() noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + + +template +simdjson_inline const T* implementation_simdjson_result_base::operator->() const noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + template simdjson_inline T& implementation_simdjson_result_base::value() & noexcept(false) { if (error()) { throw simdjson_error(error()); } @@ -26654,7 +27821,7 @@ using namespace simd; struct backslash_and_quote { public: static constexpr uint32_t BYTES_PROCESSED = 32; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); + simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; } simdjson_inline bool has_backslash() { return bs_bits != 0; } @@ -26683,6 +27850,31 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin }; } + +struct escaping { + static constexpr uint32_t BYTES_PROCESSED = 16; + simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst); + + simdjson_inline bool has_escape() { return escape_bits != 0; } + simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); } + + uint64_t escape_bits; +}; // struct escaping + + + +simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) { + static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes"); + simd8 v(src); + v.store(dst); + simd8 is_quote = (v == '"'); + simd8 is_backslash = (v == '\\'); + simd8 is_control = (v < 32); + return { + static_cast((is_backslash | is_quote | is_control).to_bitmask()) + }; +} + } // unnamed namespace } // namespace lsx } // namespace simdjson @@ -27102,12 +28294,17 @@ struct implementation_simdjson_result_base { * * @param value The variable to assign the value to. May not be set if there is an error. */ - simdjson_inline error_code get(T &value) && noexcept; + simdjson_warn_unused simdjson_inline error_code get(T &value) && noexcept; /** * The error. */ - simdjson_inline error_code error() const noexcept; + simdjson_warn_unused simdjson_inline error_code error() const noexcept; + + /** + * Whether there is a value. + */ + simdjson_warn_unused simdjson_inline bool has_value() const noexcept; #if SIMDJSON_EXCEPTIONS @@ -27116,6 +28313,16 @@ struct implementation_simdjson_result_base { * * @throw simdjson_error if there was an error. */ + simdjson_inline T& operator*() & noexcept(false); + simdjson_inline T&& operator*() && noexcept(false); + /** + * Arrow operator to access members of the contained value. + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T* operator->() noexcept(false); + simdjson_inline const T* operator->() const noexcept(false); + simdjson_inline T& value() & noexcept(false); /** @@ -27157,6 +28364,7 @@ struct implementation_simdjson_result_base { * the error() method returns a value that evaluates to false. */ simdjson_inline T&& value_unsafe() && noexcept; + protected: /** users should never directly access first and second. **/ T first{}; /** Users should never directly access 'first'. **/ @@ -27533,7 +28741,7 @@ simdjson_inline bool is_digit(const uint8_t c) { return static_cast(c - '0') <= 9; } -simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const uint8_t *const src, const uint8_t *&p, uint64_t &i, int64_t &exponent) { +simdjson_warn_unused simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const uint8_t *const src, const uint8_t *&p, uint64_t &i, int64_t &exponent) { // we continue with the fiction that we have an integer. If the // floating point number is representable as x * 10^z for some integer // z that fits in 53 bits, then we will be able to convert back the @@ -27561,7 +28769,7 @@ simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const u return SUCCESS; } -simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const src, const uint8_t *&p, int64_t &exponent) { +simdjson_warn_unused simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const src, const uint8_t *&p, int64_t &exponent) { // Exp Sign: -123.456e[-]78 bool neg_exp = ('-' == *p); if (neg_exp || '+' == *p) { p++; } // Skip + as well @@ -27650,7 +28858,7 @@ static error_code slow_float_parsing(simdjson_unused const uint8_t * src, double /** @private */ template -simdjson_inline error_code write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer) { +simdjson_warn_unused simdjson_inline error_code write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer) { // If we frequently had to deal with long strings of digits, // we could extend our code by using a 128-bit integer instead // of a 64-bit integer. However, this is uncommon in practice. @@ -27713,13 +28921,13 @@ simdjson_inline error_code write_float(const uint8_t *const src, bool negative, // // Our objective is accurate parsing (ULP of 0) at high speed. template -simdjson_inline error_code parse_number(const uint8_t *const src, W &writer); +simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const src, W &writer); // for performance analysis, it is sometimes useful to skip parsing #ifdef SIMDJSON_SKIPNUMBERPARSING template -simdjson_inline error_code parse_number(const uint8_t *const, W &writer) { +simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const, W &writer) { writer.append_s64(0); // always write zero return SUCCESS; // always succeeds } @@ -27745,7 +28953,7 @@ simdjson_unused simdjson_inline simdjson_result get_number_type(con // // Our objective is accurate parsing (ULP of 0) at high speed. template -simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { +simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { // // Check for minus sign // @@ -28529,12 +29737,41 @@ simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_b } template -simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { +simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { return this->second; } + +template +simdjson_warn_unused simdjson_inline bool implementation_simdjson_result_base::has_value() const noexcept { + return this->error() == SUCCESS; +} + #if SIMDJSON_EXCEPTIONS +template +simdjson_inline T& implementation_simdjson_result_base::operator*() & noexcept(false) { + return this->value(); +} + +template +simdjson_inline T&& implementation_simdjson_result_base::operator*() && noexcept(false) { + return std::forward>(*this).value(); +} + +template +simdjson_inline T* implementation_simdjson_result_base::operator->() noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + + +template +simdjson_inline const T* implementation_simdjson_result_base::operator->() const noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + template simdjson_inline T& implementation_simdjson_result_base::value() & noexcept(false) { if (error()) { throw simdjson_error(error()); } @@ -29196,7 +30433,7 @@ using namespace simd; struct backslash_and_quote { public: static constexpr uint32_t BYTES_PROCESSED = 32; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); + simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; } simdjson_inline bool has_backslash() { return bs_bits != 0; } @@ -29219,6 +30456,31 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin }; } + +struct escaping { + static constexpr uint32_t BYTES_PROCESSED = 16; + simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst); + + simdjson_inline bool has_escape() { return escape_bits != 0; } + simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); } + + uint64_t escape_bits; +}; // struct escaping + + + +simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) { + static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes"); + simd8 v(src); + v.store(dst); + simd8 is_quote = (v == '"'); + simd8 is_backslash = (v == '\\'); + simd8 is_control = (v < 32); + return { + (is_backslash | is_quote | is_control).to_bitmask() + }; +} + } // unnamed namespace } // namespace lasx } // namespace simdjson @@ -29638,12 +30900,17 @@ struct implementation_simdjson_result_base { * * @param value The variable to assign the value to. May not be set if there is an error. */ - simdjson_inline error_code get(T &value) && noexcept; + simdjson_warn_unused simdjson_inline error_code get(T &value) && noexcept; /** * The error. */ - simdjson_inline error_code error() const noexcept; + simdjson_warn_unused simdjson_inline error_code error() const noexcept; + + /** + * Whether there is a value. + */ + simdjson_warn_unused simdjson_inline bool has_value() const noexcept; #if SIMDJSON_EXCEPTIONS @@ -29652,6 +30919,16 @@ struct implementation_simdjson_result_base { * * @throw simdjson_error if there was an error. */ + simdjson_inline T& operator*() & noexcept(false); + simdjson_inline T&& operator*() && noexcept(false); + /** + * Arrow operator to access members of the contained value. + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T* operator->() noexcept(false); + simdjson_inline const T* operator->() const noexcept(false); + simdjson_inline T& value() & noexcept(false); /** @@ -29693,6 +30970,7 @@ struct implementation_simdjson_result_base { * the error() method returns a value that evaluates to false. */ simdjson_inline T&& value_unsafe() && noexcept; + protected: /** users should never directly access first and second. **/ T first{}; /** Users should never directly access 'first'. **/ @@ -30069,7 +31347,7 @@ simdjson_inline bool is_digit(const uint8_t c) { return static_cast(c - '0') <= 9; } -simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const uint8_t *const src, const uint8_t *&p, uint64_t &i, int64_t &exponent) { +simdjson_warn_unused simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const uint8_t *const src, const uint8_t *&p, uint64_t &i, int64_t &exponent) { // we continue with the fiction that we have an integer. If the // floating point number is representable as x * 10^z for some integer // z that fits in 53 bits, then we will be able to convert back the @@ -30097,7 +31375,7 @@ simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const u return SUCCESS; } -simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const src, const uint8_t *&p, int64_t &exponent) { +simdjson_warn_unused simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const src, const uint8_t *&p, int64_t &exponent) { // Exp Sign: -123.456e[-]78 bool neg_exp = ('-' == *p); if (neg_exp || '+' == *p) { p++; } // Skip + as well @@ -30186,7 +31464,7 @@ static error_code slow_float_parsing(simdjson_unused const uint8_t * src, double /** @private */ template -simdjson_inline error_code write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer) { +simdjson_warn_unused simdjson_inline error_code write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer) { // If we frequently had to deal with long strings of digits, // we could extend our code by using a 128-bit integer instead // of a 64-bit integer. However, this is uncommon in practice. @@ -30249,13 +31527,13 @@ simdjson_inline error_code write_float(const uint8_t *const src, bool negative, // // Our objective is accurate parsing (ULP of 0) at high speed. template -simdjson_inline error_code parse_number(const uint8_t *const src, W &writer); +simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const src, W &writer); // for performance analysis, it is sometimes useful to skip parsing #ifdef SIMDJSON_SKIPNUMBERPARSING template -simdjson_inline error_code parse_number(const uint8_t *const, W &writer) { +simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const, W &writer) { writer.append_s64(0); // always write zero return SUCCESS; // always succeeds } @@ -30281,7 +31559,7 @@ simdjson_unused simdjson_inline simdjson_result get_number_type(con // // Our objective is accurate parsing (ULP of 0) at high speed. template -simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { +simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { // // Check for minus sign // @@ -31065,12 +32343,41 @@ simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_b } template -simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { +simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { return this->second; } + +template +simdjson_warn_unused simdjson_inline bool implementation_simdjson_result_base::has_value() const noexcept { + return this->error() == SUCCESS; +} + #if SIMDJSON_EXCEPTIONS +template +simdjson_inline T& implementation_simdjson_result_base::operator*() & noexcept(false) { + return this->value(); +} + +template +simdjson_inline T&& implementation_simdjson_result_base::operator*() && noexcept(false) { + return std::forward>(*this).value(); +} + +template +simdjson_inline T* implementation_simdjson_result_base::operator->() noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + + +template +simdjson_inline const T* implementation_simdjson_result_base::operator->() const noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + template simdjson_inline T& implementation_simdjson_result_base::value() & noexcept(false) { if (error()) { throw simdjson_error(error()); } @@ -31162,6 +32469,7 @@ simdjson_inline implementation_simdjson_result_base::implementation_simdjson_ // Internal headers needed for ondemand generics. // All includes not under simdjson/generic/ondemand must be here! // Otherwise, amalgamation will fail. +/* skipped duplicate #include "simdjson/concepts.h" */ /* skipped duplicate #include "simdjson/dom/base.h" // for MINIMAL_DOCUMENT_CAPACITY */ /* skipped duplicate #include "simdjson/implementation.h" */ /* skipped duplicate #include "simdjson/padded_string.h" */ @@ -31596,6 +32904,12 @@ namespace { tmp = vpaddq_u8(tmp, tmp); return vgetq_lane_u16(vreinterpretq_u16_u8(tmp), 0); } + // Returns 4-bit out of each byte, alternating between the high 4 bits and low + // bits result it is 64 bit. + simdjson_inline uint64_t to_bitmask64() const { + return vget_lane_u64( + vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(*this), 4)), 0); + } simdjson_inline bool any() const { return vmaxvq_u32(vreinterpretq_u32_u8(*this)) != 0; } }; @@ -31672,7 +32986,7 @@ namespace { // Bit-specific operations simdjson_inline simd8 any_bits_set(simd8 bits) const { return vtstq_u8(*this, bits); } - simdjson_inline bool any_bits_set_anywhere() const { return this->max_val() != 0; } + simdjson_inline bool any_bits_set_anywhere() const { return vmaxvq_u32(vreinterpretq_u32_u8(*this)) != 0; } simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { return (*this & bits).any_bits_set_anywhere(); } template simdjson_inline simd8 shr() const { return vshrq_n_u8(*this, N); } @@ -31685,7 +32999,12 @@ namespace { return lookup_table.apply_lookup_16_to(*this); } - + // Returns 4-bit out of each byte, alternating between the high 4 bits and low + // bits result it is 64 bit. + simdjson_inline uint64_t to_bitmask64() const { + return vget_lane_u64( + vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(*this), 4)), 0); + } // Copies to 'output" all bytes corresponding to a 0 in the mask (interpreted as a bitset). // Passing a 0 value for mask would be equivalent to writing out every byte to output. // Only the first 16 - count_ones(mask) bytes of the result are significant but 16 bytes @@ -31979,7 +33298,7 @@ using namespace simd; struct backslash_and_quote { public: static constexpr uint32_t BYTES_PROCESSED = 32; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); + simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; } simdjson_inline bool has_backslash() { return bs_bits != 0; } @@ -32008,6 +33327,32 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin }; } +struct escaping { + static constexpr uint32_t BYTES_PROCESSED = 16; + simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst); + + simdjson_inline bool has_escape() { return escape_bits != 0; } + simdjson_inline int escape_index() { return trailing_zeroes(escape_bits) / 4; } + + uint64_t escape_bits; +}; // struct escaping + + + +simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) { + static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes"); + simd8 v(src); + v.store(dst); + simd8 is_quote = (v == '"'); + simd8 is_backslash = (v == '\\'); + simd8 is_control = (v < 32); + return { + (is_backslash | is_quote | is_control).to_bitmask64() + }; +} + + + } // unnamed namespace } // namespace arm64 } // namespace simdjson @@ -32076,7 +33421,7 @@ class value_iterator; /* end file simdjson/generic/ondemand/base.h for arm64 */ /* including simdjson/generic/ondemand/deserialize.h for arm64: #include "simdjson/generic/ondemand/deserialize.h" */ /* begin file simdjson/generic/ondemand/deserialize.h for arm64 */ -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS #ifndef SIMDJSON_ONDEMAND_DESERIALIZE_H /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ @@ -32085,55 +33430,8 @@ class value_iterator; /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ -#include namespace simdjson { -namespace tag_invoke_fn_ns { -void tag_invoke(); - -struct tag_invoke_fn { - template - requires requires(Tag tag, Args &&...args) { - tag_invoke(std::forward(tag), std::forward(args)...); - } - constexpr auto operator()(Tag tag, Args &&...args) const - noexcept(noexcept(tag_invoke(std::forward(tag), - std::forward(args)...))) - -> decltype(tag_invoke(std::forward(tag), - std::forward(args)...)) { - return tag_invoke(std::forward(tag), std::forward(args)...); - } -}; -} // namespace tag_invoke_fn_ns - -inline namespace tag_invoke_ns { -inline constexpr tag_invoke_fn_ns::tag_invoke_fn tag_invoke = {}; -} // namespace tag_invoke_ns - -template -concept tag_invocable = requires(Tag tag, Args... args) { - tag_invoke(std::forward(tag), std::forward(args)...); -}; - -template -concept nothrow_tag_invocable = - tag_invocable && requires(Tag tag, Args... args) { - { - tag_invoke(std::forward(tag), std::forward(args)...) - } noexcept; - }; - -template -using tag_invoke_result = - std::invoke_result; - -template -using tag_invoke_result_t = - std::invoke_result_t; - -template using tag_t = std::decay_t; - - struct deserialize_tag; /// These types are deserializable in a built-in way @@ -32155,7 +33453,7 @@ template concept custom_deserializable = tag_invocable; template -concept deserializable = custom_deserializable || is_builtin_deserializable_v; +concept deserializable = custom_deserializable || is_builtin_deserializable_v || concepts::optional_type; template concept nothrow_custom_deserializable = nothrow_tag_invocable; @@ -32166,28 +33464,44 @@ concept nothrow_deserializable = nothrow_custom_deserializable || is_bu /// Deserialize Tag inline constexpr struct deserialize_tag { + using array_type = arm64::ondemand::array; + using object_type = arm64::ondemand::object; using value_type = arm64::ondemand::value; using document_type = arm64::ondemand::document; using document_reference_type = arm64::ondemand::document_reference; + // Customization Point for array + template + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(array_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for object + template + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(object_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + // Customization Point for value template requires custom_deserializable - [[nodiscard]] constexpr /* error_code */ auto operator()(value_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + simdjson_warn_unused constexpr /* error_code */ auto operator()(value_type &object, T& output) const noexcept(nothrow_custom_deserializable) { return tag_invoke(*this, object, output); } // Customization Point for document template requires custom_deserializable - [[nodiscard]] constexpr /* error_code */ auto operator()(document_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + simdjson_warn_unused constexpr /* error_code */ auto operator()(document_type &object, T& output) const noexcept(nothrow_custom_deserializable) { return tag_invoke(*this, object, output); } // Customization Point for document reference template requires custom_deserializable - [[nodiscard]] constexpr /* error_code */ auto operator()(document_reference_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + simdjson_warn_unused constexpr /* error_code */ auto operator()(document_reference_type &object, T& output) const noexcept(nothrow_custom_deserializable) { return tag_invoke(*this, object, output); } @@ -32197,7 +33511,7 @@ inline constexpr struct deserialize_tag { } // namespace simdjson #endif // SIMDJSON_ONDEMAND_DESERIALIZE_H -#endif // SIMDJSON_SUPPORTS_DESERIALIZATION +#endif // SIMDJSON_SUPPORTS_CONCEPTS /* end file simdjson/generic/ondemand/deserialize.h for arm64 */ /* including simdjson/generic/ondemand/value_iterator.h for arm64: #include "simdjson/generic/ondemand/value_iterator.h" */ @@ -32539,7 +33853,7 @@ class value_iterator { simdjson_warn_unused simdjson_inline simdjson_result get_root_number(bool check_trailing) noexcept; simdjson_warn_unused simdjson_inline simdjson_result is_root_null(bool check_trailing) noexcept; - simdjson_inline error_code error() const noexcept; + simdjson_warn_unused simdjson_inline error_code error() const noexcept; simdjson_inline uint8_t *&string_buf_loc() noexcept; simdjson_inline const json_iterator &json_iter() const noexcept; simdjson_inline json_iterator &json_iter() noexcept; @@ -32623,8 +33937,8 @@ class value_iterator { simdjson_inline const uint8_t *peek_non_root_scalar(const char *type) noexcept; - simdjson_inline error_code start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept; - simdjson_inline error_code end_container() noexcept; + simdjson_warn_unused simdjson_inline error_code start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept; + simdjson_warn_unused simdjson_inline error_code end_container() noexcept; /** * Advance to a place expecting a value (increasing depth). @@ -32634,8 +33948,8 @@ class value_iterator { */ simdjson_inline simdjson_result advance_to_value() noexcept; - simdjson_inline error_code incorrect_type_error(const char *message) const noexcept; - simdjson_inline error_code error_unless_more_tokens(uint32_t tokens=1) const noexcept; + simdjson_warn_unused simdjson_inline error_code incorrect_type_error(const char *message) const noexcept; + simdjson_warn_unused simdjson_inline error_code error_unless_more_tokens(uint32_t tokens=1) const noexcept; simdjson_inline bool is_at_start() const noexcept; /** @@ -32672,7 +33986,7 @@ class value_iterator { /** @copydoc error_code json_iterator::end_position() const noexcept; */ simdjson_inline token_position end_position() const noexcept; /** @copydoc error_code json_iterator::report_error(error_code error, const char *message) noexcept; */ - simdjson_inline error_code report_error(error_code error, const char *message) noexcept; + simdjson_warn_unused simdjson_inline error_code report_error(error_code error, const char *message) noexcept; friend class document; friend class object; @@ -32737,13 +34051,14 @@ class value { * * You may use get_double(), get_bool(), get_uint64(), get_int64(), * get_object(), get_array(), get_raw_json_string(), or get_string() instead. + * When SIMDJSON_SUPPORTS_CONCEPTS is set, custom types are also supported. * * @returns A value of the given type, parsed from the JSON. * @returns INCORRECT_TYPE If the JSON value is not the given type. */ template simdjson_inline simdjson_result get() -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept @@ -32760,22 +34075,38 @@ class value { * Get this value as the given type. * * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool + * If the macro SIMDJSON_SUPPORTS_CONCEPTS is set, then custom types are also supported. * * @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized. * @returns INCORRECT_TYPE If the JSON value is not an object. * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. */ template - simdjson_inline error_code get(T &out) -#if SIMDJSON_SUPPORTS_DESERIALIZATION + simdjson_warn_unused simdjson_inline error_code get(T &out) +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept #endif { - #if SIMDJSON_SUPPORTS_DESERIALIZATION + #if SIMDJSON_SUPPORTS_CONCEPTS if constexpr (custom_deserializable) { return deserialize(*this, out); + } else if constexpr (concepts::optional_type) { + using value_type = typename std::remove_cvref_t::value_type; + + // Check if the value is null + bool is_null_value; + SIMDJSON_TRY( is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); // Set to nullopt + return SUCCESS; + } + + if (!out) { + out.emplace(); + } + return get(out.value()); } else { static_assert(!sizeof(T), "The get method with type T is not implemented by the simdjson library. " "And you do not seem to have added support for it. Indeed, we have that " @@ -32785,7 +34116,7 @@ class value { static_cast(out); // to get rid of unused errors return UNINITIALIZED; } -#else // SIMDJSON_SUPPORTS_DESERIALIZATION +#else // SIMDJSON_SUPPORTS_CONCEPTS // Unless the simdjson library or the user provides an inline implementation, calling this method should // immediately fail. static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " @@ -32903,7 +34234,7 @@ class value { * @returns INCORRECT_TYPE if the JSON value is not a string. Otherwise, we return SUCCESS. */ template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; /** * Cast this JSON value to a "wobbly" string. @@ -33424,7 +34755,7 @@ struct simdjson_result : public arm64::implementation_si simdjson_inline simdjson_result get_double_in_string() noexcept; simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; simdjson_inline simdjson_result get_wobbly_string() noexcept; simdjson_inline simdjson_result get_raw_json_string() noexcept; simdjson_inline simdjson_result get_bool() noexcept; @@ -33505,7 +34836,22 @@ struct simdjson_result : public arm64::implementation_si simdjson_result operator[](int) noexcept = delete; /** - * Get the type of this JSON value. + * Get the type of this JSON value. It does not validate or consume the value. + * E.g., you must still call "is_null()" to check that a value is null even if + * "type()" returns json_type::null. + * + * Given a valid JSON document, the answer can be one of + * simdjson::ondemand::json_type::object, + * simdjson::ondemand::json_type::array, + * simdjson::ondemand::json_type::string, + * simdjson::ondemand::json_type::number, + * simdjson::ondemand::json_type::boolean, + * simdjson::ondemand::json_type::null. + * + * Starting with simdjson 4.0, this function will return simdjson::ondemand::json_type::unknown + * given a bad token. + * This allows you to identify a case such as {"key": NaN} and identify the NaN value. + * The simdjson::ondemand::json_type::unknown value should only happen with non-valid JSON. * * NOTE: If you're only expecting a value to be one type (a typical case), it's generally * better to just call .get_double, .get_string, etc. and check for INCORRECT_TYPE (or just @@ -33999,14 +35345,14 @@ class json_iterator { * @param error The error to report. Must not be SUCCESS, UNINITIALIZED, INCORRECT_TYPE, or NO_SUCH_FIELD. * @param message An error message to report with the error. */ - simdjson_inline error_code report_error(error_code error, const char *message) noexcept; + simdjson_warn_unused simdjson_inline error_code report_error(error_code error, const char *message) noexcept; /** * Log error, but don't stop iteration. * @param error The error to report. Must be INCORRECT_TYPE, or NO_SUCH_FIELD. * @param message An error message to report with the error. */ - simdjson_inline error_code optional_error(error_code error, const char *message) noexcept; + simdjson_warn_unused simdjson_inline error_code optional_error(error_code error, const char *message) noexcept; /** * Take an input in json containing max_len characters and attempt to copy it over to tmpbuf, a buffer with @@ -34026,7 +35372,7 @@ class json_iterator { simdjson_inline void reenter_child(token_position position, depth_t child_depth) noexcept; - simdjson_inline error_code consume_character(char c) noexcept; + simdjson_warn_unused simdjson_inline error_code consume_character(char c) noexcept; #if SIMDJSON_DEVELOPMENT_CHECKS simdjson_inline token_position start_position(depth_t depth) const noexcept; simdjson_inline void set_start_position(depth_t depth, token_position position) noexcept; @@ -34117,6 +35463,7 @@ namespace ondemand { * The type of a JSON value. */ enum class json_type { + unknown=0, // Start at 1 to catch uninitialized / default values more easily array=1, ///< A JSON array ( [ 1, 2, 3 ... ] ) object, ///< A JSON object ( { "a": 1, "b" 2, ... } ) @@ -34323,6 +35670,12 @@ class raw_json_string { */ simdjson_inline const char * raw() const noexcept; + /** + * Get the character at index i. This is unchecked. + * [0] when the string is of length 0 returns the final quote ("). + */ + simdjson_inline char operator[](size_t i) const noexcept; + /** * This compares the current instance to the std::string_view target: returns true if * they are byte-by-byte equal (no escaping is done) on target.size() characters, @@ -34462,10 +35815,10 @@ struct simdjson_result : public arm64::impleme simdjson_inline ~simdjson_result() noexcept = default; ///< @private simdjson_inline simdjson_result raw() const noexcept; + simdjson_inline char operator[](size_t) const noexcept; simdjson_inline simdjson_warn_unused simdjson_result unescape(arm64::ondemand::json_iterator &iter, bool allow_replacement) const noexcept; simdjson_inline simdjson_warn_unused simdjson_result unescape_wobbly(arm64::ondemand::json_iterator &iter) const noexcept; }; - } // namespace simdjson #endif // SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_H @@ -34481,6 +35834,7 @@ struct simdjson_result : public arm64::impleme /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ #include +#include namespace simdjson { namespace arm64 { @@ -34599,7 +35953,9 @@ class parser { simdjson_warn_unused simdjson_result iterate(std::string_view json, size_t capacity) & noexcept; /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ simdjson_warn_unused simdjson_result iterate(const std::string &json) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + /** @overload simdjson_result iterate(padded_string_view json) & noexcept + The string instance might be have its capacity extended. Note that this can still + result in AddressSanitizer: container-overflow in some cases. */ simdjson_warn_unused simdjson_result iterate(std::string &json) & noexcept; /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ simdjson_warn_unused simdjson_result iterate(const simdjson_result &json) & noexcept; @@ -34687,6 +36043,11 @@ class parser { * Setting batch_size to excessively large or excessively small values may impact negatively the * performance. * + * ### Threads + * + * When compiled with SIMDJSON_THREADS_ENABLED, this method will use a single thread under the + * hood to do some lookahead. + * * ### REQUIRED: Buffer Padding * * The buffer must have at least SIMDJSON_PADDING extra allocated bytes. It does not matter what @@ -34694,10 +36055,10 @@ class parser { * using a sanitizer that verifies that no uninitialized byte is read, then you should initialize the * SIMDJSON_PADDING bytes to avoid runtime warnings. * - * ### Threads + * This is checked automatically with all iterate_many function calls, except for the two + * that take pointers (const char* or const uint8_t*). * - * When compiled with SIMDJSON_THREADS_ENABLED, this method will use a single thread under the - * hood to do some lookahead. + * ### Threads * * ### Parser Capacity * @@ -34723,14 +36084,16 @@ class parser { */ inline simdjson_result iterate_many(const uint8_t *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ + inline simdjson_result iterate_many(padded_string_view json, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ inline simdjson_result iterate_many(const char *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ inline simdjson_result iterate_many(const std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; - inline simdjson_result iterate_many(const std::string &&s, size_t batch_size, bool allow_comma_separated = false) = delete;// unsafe + /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) + the string might be automatically padded with up to SIMDJSON_PADDING whitespace characters */ + inline simdjson_result iterate_many(std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ inline simdjson_result iterate_many(const padded_string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; - inline simdjson_result iterate_many(const padded_string &&s, size_t batch_size, bool allow_comma_separated = false) = delete;// unsafe - /** @private We do not want to allow implicit conversion from C string to std::string. */ simdjson_result iterate_many(const char *buf, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept = delete; @@ -34832,13 +36195,39 @@ class parser { bool string_buffer_overflow(const uint8_t *string_buf_loc) const noexcept; #endif + /** + * Get a unique parser instance corresponding to the current thread. + * This instance can be safely used within the current thread, but it should + * not be passed to other threads. + * + * A parser should only be used for one document at a time. + * + * Our simdjson::from functions use this parser instance. + * + * You can free the related parser by calling release_parser(). + */ + static simdjson_inline simdjson_warn_unused ondemand::parser& get_parser(); + /** + * Release the parser instance initialized by get_parser() and all the + * associated resources (memory). Returns true if a parser instance + * was released. + */ + static simdjson_inline bool release_parser(); + private: + friend bool release_parser(); + friend ondemand::parser& get_parser(); + /** Get the thread-local parser instance, allocates it if needed */ + static simdjson_inline simdjson_warn_unused std::unique_ptr& get_parser_instance(); + /** Get the thread-local parser instance, it might be null */ + static simdjson_inline simdjson_warn_unused std::unique_ptr& get_threadlocal_parser_if_exists(); /** @private [for benchmarking access] The implementation to use */ std::unique_ptr implementation{}; size_t _capacity{0}; size_t _max_capacity; size_t _max_depth{DEFAULT_MAX_DEPTH}; std::unique_ptr string_buf{}; + #if SIMDJSON_DEVELOPMENT_CHECKS std::unique_ptr start_positions{}; #endif @@ -34866,6 +36255,315 @@ struct simdjson_result : public arm64::implementation_s #endif // SIMDJSON_GENERIC_ONDEMAND_PARSER_H /* end file simdjson/generic/ondemand/parser.h for arm64 */ +// JSON builder - needed for extract_into functionality +/* including simdjson/generic/ondemand/json_string_builder.h for arm64: #include "simdjson/generic/ondemand/json_string_builder.h" */ +/* begin file simdjson/generic/ondemand/json_string_builder.h for arm64 */ +/** + * This file is part of the builder API. It is temporarily in the ondemand directory + * but we will move it to a builder directory later. + */ +#ifndef SIMDJSON_GENERIC_STRING_BUILDER_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { + + +#if SIMDJSON_SUPPORTS_CONCEPTS + +namespace arm64 { +namespace builder { + class string_builder; +}} + +template +struct has_custom_serialization : std::false_type {}; + +inline constexpr struct serialize_tag { + template + requires custom_deserializable + constexpr void operator()(arm64::builder::string_builder& b, T& obj) const{ + return tag_invoke(*this, b, obj); + } + + +} serialize{}; +template +struct has_custom_serialization(), std::declval())) +>> : std::true_type {}; + +template +constexpr bool require_custom_serialization = has_custom_serialization::value; +#else +struct has_custom_serialization : std::false_type {}; +#endif // SIMDJSON_SUPPORTS_CONCEPTS + +namespace arm64 { +namespace builder { +/** + * A builder for JSON strings representing documents. This is a low-level + * builder that is not meant to be used directly by end-users. Though it + * supports atomic types (Booleans, strings), it does not support composed + * types (arrays and objects). + * + * Ultimately, this class can support kernel-specific optimizations. E.g., + * it may make use of SIMD instructions to escape strings faster. + */ +class string_builder { +public: + simdjson_inline string_builder(size_t initial_capacity = DEFAULT_INITIAL_CAPACITY); + + static constexpr size_t DEFAULT_INITIAL_CAPACITY = 1024; + + /** + * Append number (includes Booleans). Booleans are mapped to the strings + * false and true. Numbers are converted to strings abiding by the JSON standard. + * Floating-point numbers are converted to the shortest string that 'correctly' + * represents the number. + */ + template::value>::type> + simdjson_inline void append(number_type v) noexcept; + + /** + * Append character c. + */ + simdjson_inline void append(char c) noexcept; + + /** + * Append the string 'null'. + */ + simdjson_inline void append_null() noexcept; + + /** + * Clear the content. + */ + simdjson_inline void clear() noexcept; + + /** + * Append the std::string_view, after escaping it. + * There is no UTF-8 validation. + */ + simdjson_inline void escape_and_append(std::string_view input) noexcept; + + /** + * Append the std::string_view surrounded by double quotes, after escaping it. + * There is no UTF-8 validation. + */ + simdjson_inline void escape_and_append_with_quotes(std::string_view input) noexcept; +#if SIMDJSON_SUPPORTS_CONCEPTS + template + simdjson_inline void escape_and_append_with_quotes() noexcept; +#endif + /** + * Append the character surrounded by double quotes, after escaping it. + * There is no UTF-8 validation. + */ + simdjson_inline void escape_and_append_with_quotes(char input) noexcept; + + /** + * Append the character surrounded by double quotes, after escaping it. + * There is no UTF-8 validation. + */ + simdjson_inline void escape_and_append_with_quotes(const char* input) noexcept; + + /** + * Append the C string directly, without escaping. + * There is no UTF-8 validation. + */ + simdjson_inline void append_raw(const char *c) noexcept; + + /** + * Append "{" to the buffer. + */ + simdjson_inline void start_object() noexcept; + + /** + * Append "}" to the buffer. + */ + simdjson_inline void end_object() noexcept; + + /** + * Append "[" to the buffer. + */ + simdjson_inline void start_array() noexcept; + + /** + * Append "]" to the buffer. + */ + simdjson_inline void end_array() noexcept; + + /** + * Append "," to the buffer. + */ + simdjson_inline void append_comma() noexcept; + + /** + * Append ":" to the buffer. + */ + simdjson_inline void append_colon() noexcept; + + /** + * Append a key-value pair to the buffer. + * The key is escaped and surrounded by double quotes. + * The value is escaped if it is a string. + */ + template + simdjson_inline void append_key_value(key_type key, value_type value) noexcept; +#if SIMDJSON_SUPPORTS_CONCEPTS + template + simdjson_inline void append_key_value(value_type value) noexcept; + + // Support for optional types (std::optional, etc.) + template + requires(!require_custom_serialization) + simdjson_inline void append(const T &opt); + + template + requires(require_custom_serialization) + simdjson_inline void append(const T &val); + + // Support for string-like types + template + requires(std::is_convertible::value || + std::is_same::value ) + simdjson_inline void append(const T &value); +#endif +#if SIMDJSON_SUPPORTS_RANGES && SIMDJSON_SUPPORTS_CONCEPTS + // Support for range-based appending (std::ranges::view, etc.) + template +requires (!std::is_convertible::value) + simdjson_inline void append(const R &range) noexcept; +#endif + /** + * Append the std::string_view directly, without escaping. + * There is no UTF-8 validation. + */ + simdjson_inline void append_raw(std::string_view input) noexcept; + + /** + * Append len characters from str. + * There is no UTF-8 validation. + */ + simdjson_inline void append_raw(const char *str, size_t len) noexcept; +#if SIMDJSON_EXCEPTIONS + /** + * Creates an std::string from the written JSON buffer. + * Throws if memory allocation failed + * + * The result may not be valid UTF-8 if some of your content was not valid UTF-8. + * Use validate_unicode() to check the content if needed. + */ + simdjson_inline operator std::string() const noexcept(false); + + /** + * Creates an std::string_view from the written JSON buffer. + * Throws if memory allocation failed. + * + * The result may not be valid UTF-8 if some of your content was not valid UTF-8. + * Use validate_unicode() to check the content if needed. + */ + simdjson_inline operator std::string_view() const noexcept(false) simdjson_lifetime_bound; +#endif + + /** + * Returns a view on the written JSON buffer. Returns an error + * if memory allocation failed. + * + * The result may not be valid UTF-8 if some of your content was not valid UTF-8. + * Use validate_unicode() to check the content. + */ + simdjson_inline simdjson_result view() const noexcept; + + /** + * Appends the null character to the buffer and returns + * a pointer to the beginning of the written JSON buffer. + * Returns an error if memory allocation failed. + * The result is null-terminated. + * + * The result may not be valid UTF-8 if some of your content was not valid UTF-8. + * Use validate_unicode() to check the content. + */ + simdjson_inline simdjson_result c_str() noexcept; + + /** + * Return true if the content is valid UTF-8. + */ + simdjson_inline bool validate_unicode() const noexcept; + + /** + * Returns the current size of the written JSON buffer. + * If an error occurred, returns 0. + */ + simdjson_inline size_t size() const noexcept; + +private: + /** + * Returns true if we can write at least upcoming_bytes bytes. + * The underlying buffer is reallocated if needed. It is designed + * to be called before writing to the buffer. It should be fast. + */ + simdjson_inline bool capacity_check(size_t upcoming_bytes); + + /** + * Grow the buffer to at least desired_capacity bytes. + * If the allocation fails, is_valid is set to false. We expect + * that this function would not be repeatedly called. + */ + simdjson_inline void grow_buffer(size_t desired_capacity); + + /** + * We use this helper function to make sure that is_valid is kept consistent. + */ + simdjson_inline void set_valid(bool valid) noexcept; + + std::unique_ptr buffer{}; + size_t position{0}; + size_t capacity{0}; + bool is_valid{true}; +}; + + + +} +} + + +#if !SIMDJSON_STATIC_REFLECTION +// fallback implementation until we have static reflection +template +simdjson_warn_unused simdjson_result to_json(const Z &z, size_t initial_capacity = simdjson::arm64::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { + simdjson::arm64::builder::string_builder b(initial_capacity); + b.append(z); + std::string_view s; + auto e = b.view().get(s); + if(e) { return e; } + return std::string(s); +} +template +simdjson_warn_unused simdjson_error to_json(const Z &z, std::string &s, size_t initial_capacity = simdjson::arm64::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { + simdjson::arm64::builder::string_builder b(initial_capacity); + b.append(z); + std::string_view sv; + auto e = b.view().get(sv); + if(e) { return e; } + s.assign(sv.data(), sv.size()); + return simdjson::SUCCESS; +} +#endif + +#if SIMDJSON_SUPPORTS_CONCEPTS +#endif // SIMDJSON_SUPPORTS_CONCEPTS + +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_STRING_BUILDER_H +/* end file simdjson/generic/ondemand/json_string_builder.h for arm64 */ + // All other declarations /* including simdjson/generic/ondemand/array.h for arm64: #include "simdjson/generic/ondemand/array.h" */ /* begin file simdjson/generic/ondemand/array.h for arm64 */ @@ -35002,11 +36700,42 @@ class array { * - INDEX_OUT_OF_BOUNDS if the array index is larger than an array length */ simdjson_inline simdjson_result at(size_t index) noexcept; + +#if SIMDJSON_SUPPORTS_CONCEPTS + /** + * Get this array as the given type. + * + * @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized. + * @returns INCORRECT_TYPE If the JSON array is not of the given type. + * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. + */ + template + simdjson_warn_unused simdjson_inline error_code get(T &out) + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) { + static_assert(custom_deserializable); + return deserialize(*this, out); + } + /** + * Get this array as the given type. + * + * @returns A value of the given type, parsed from the JSON. + * @returns INCORRECT_TYPE If the JSON value is not the given type. + */ + template + simdjson_inline simdjson_result get() + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) + { + static_assert(std::is_default_constructible::value, "The specified type is not default constructible."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } +#endif // SIMDJSON_SUPPORTS_CONCEPTS protected: /** * Go to the end of the array, no matter where you are right now. */ - simdjson_inline error_code consume() noexcept; + simdjson_warn_unused simdjson_inline error_code consume() noexcept; /** * Begin array iteration. @@ -35080,7 +36809,28 @@ struct simdjson_result : public arm64::implementation_si simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; simdjson_inline simdjson_result raw_json() noexcept; +#if SIMDJSON_SUPPORTS_CONCEPTS + // TODO: move this code into object-inl.h + template + simdjson_inline simdjson_result get() noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + return first; + } + return first.get(); + } + template + simdjson_warn_unused simdjson_inline error_code get(T& out) noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + out = first; + } else { + SIMDJSON_TRY( first.get(out) ); + } + return SUCCESS; + } +#endif // SIMDJSON_SUPPORTS_CONCEPTS }; } // namespace simdjson @@ -35125,7 +36875,8 @@ class array_iterator { * * Part of the std::iterator interface. */ - simdjson_inline simdjson_result operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. + simdjson_inline simdjson_result + operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. /** * Check if we are at the end of the JSON. * @@ -35149,6 +36900,11 @@ class array_iterator { */ simdjson_inline array_iterator &operator++() noexcept; + /** + * Check if the array is at the end. + */ + simdjson_warn_unused simdjson_inline bool at_end() const noexcept; + private: value_iterator iter{}; @@ -35167,7 +36923,6 @@ namespace simdjson { template<> struct simdjson_result : public arm64::implementation_simdjson_result_base { -public: simdjson_inline simdjson_result(arm64::ondemand::array_iterator &&value) noexcept; ///< @private simdjson_inline simdjson_result(error_code error) noexcept; ///< @private simdjson_inline simdjson_result() noexcept = default; @@ -35180,6 +36935,8 @@ struct simdjson_result : public arm64::implemen simdjson_inline bool operator==(const simdjson_result &) const noexcept; simdjson_inline bool operator!=(const simdjson_result &) const noexcept; simdjson_inline simdjson_result &operator++() noexcept; + + simdjson_warn_unused simdjson_inline bool at_end() const noexcept; }; } // namespace simdjson @@ -35307,7 +37064,7 @@ class document { * @returns INCORRECT_TYPE if the JSON value is not a string. Otherwise, we return SUCCESS. */ template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; /** * Cast this JSON value to a string. * @@ -35373,7 +37130,7 @@ class document { */ template simdjson_inline simdjson_result get() & -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept @@ -35396,7 +37153,7 @@ class document { */ template simdjson_inline simdjson_result get() && -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept @@ -35414,18 +37171,18 @@ class document { * Be mindful that the document instance must remain in scope while you are accessing object, array and value instances. * * @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized. - * @returns INCORRECT_TYPE If the JSON value is not an object. + * @returns INCORRECT_TYPE If the JSON value is of the given type. * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. */ template - simdjson_inline error_code get(T &out) & -#if SIMDJSON_SUPPORTS_DESERIALIZATION + simdjson_warn_unused simdjson_inline error_code get(T &out) & +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept #endif { -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS if constexpr (custom_deserializable) { return deserialize(*this, out); } else { @@ -35437,7 +37194,7 @@ class document { static_cast(out); // to get rid of unused errors return UNINITIALIZED; } -#else // SIMDJSON_SUPPORTS_DESERIALIZATION +#else // SIMDJSON_SUPPORTS_CONCEPTS // Unless the simdjson library or the user provides an inline implementation, calling this method should // immediately fail. static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " @@ -35447,7 +37204,7 @@ class document { " You may also add support for custom types, see our documentation."); static_cast(out); // to get rid of unused errors return UNINITIALIZED; -#endif // SIMDJSON_SUPPORTS_DESERIALIZATION +#endif // SIMDJSON_SUPPORTS_CONCEPTS } /** @overload template error_code get(T &out) & noexcept */ @@ -35512,7 +37269,7 @@ class document { * time it parses a document or when it is destroyed. * @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string. */ - simdjson_inline operator std::string_view() noexcept(false); + simdjson_inline operator std::string_view() noexcept(false) simdjson_lifetime_bound; /** * Cast this JSON value to a raw_json_string. * @@ -35521,7 +37278,7 @@ class document { * @returns A pointer to the raw JSON for the given string. * @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string. */ - simdjson_inline operator raw_json_string() noexcept(false); + simdjson_inline operator raw_json_string() noexcept(false) simdjson_lifetime_bound; /** * Cast this JSON value to a bool. * @@ -35671,11 +37428,27 @@ class document { * E.g., you must still call "is_null()" to check that a value is null even if * "type()" returns json_type::null. * + * The answer can be one of + * simdjson::ondemand::json_type::object, + * simdjson::ondemand::json_type::array, + * simdjson::ondemand::json_type::string, + * simdjson::ondemand::json_type::number, + * simdjson::ondemand::json_type::boolean, + * simdjson::ondemand::json_type::null. + * + * Starting with simdjson 4.0, this function will return simdjson::ondemand::json_type::unknown + * given a bad token. + * This allows you to identify a case such as {"key": NaN} and identify the NaN value. + * The simdjson::ondemand::json_type::unknown value should only happen with non-valid JSON. + * * NOTE: If you're only expecting a value to be one type (a typical case), it's generally * better to just call .get_double, .get_string, etc. and check for INCORRECT_TYPE (or just * let it throw an exception). * - * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". + * Prior to simdjson 4.0, this function would return an error given a bad token. + * Starting with simdjson 4.0, it will return simdjson::ondemand::json_type::unknown. + * This allows you to identify a case such as {"key": NaN} and identify the NaN value. + * The simdjson::ondemand::json_type::unknown value should only happen with non-valid JSON. */ simdjson_inline simdjson_result type() noexcept; @@ -35899,11 +37672,41 @@ class document { * the JSON document. */ simdjson_inline simdjson_result raw_json() noexcept; + +#if SIMDJSON_STATIC_REFLECTION + /** + * Extract only specific fields from the JSON object into a struct. + * + * This allows selective deserialization of only the fields you need, + * potentially improving performance by skipping unwanted fields. + * + * Example: + * ```c++ + * struct Car { + * std::string make; + * std::string model; + * int year; + * double price; + * }; + * + * Car car; + * doc.extract_into<"make", "model">(car); + * // Only 'make' and 'model' fields are extracted from JSON + * ``` + * + * @tparam FieldNames Compile-time string literals specifying which fields to extract + * @param out The output struct to populate with selected fields + * @returns SUCCESS on success, or an error code if a required field is missing or has wrong type + */ + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept; +#endif // SIMDJSON_STATIC_REFLECTION protected: /** * Consumes the document. */ - simdjson_inline error_code consume() noexcept; + simdjson_warn_unused simdjson_inline error_code consume() noexcept; simdjson_inline document(ondemand::json_iterator &&iter) noexcept; simdjson_inline const uint8_t *text(uint32_t idx) const noexcept; @@ -35956,7 +37759,7 @@ class document_reference { simdjson_inline simdjson_result get_double_in_string() noexcept; simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; simdjson_inline simdjson_result get_wobbly_string() noexcept; simdjson_inline simdjson_result get_raw_json_string() noexcept; simdjson_inline simdjson_result get_bool() noexcept; @@ -35965,7 +37768,7 @@ class document_reference { simdjson_inline simdjson_result is_null() noexcept; template simdjson_inline simdjson_result get() & -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept @@ -35978,7 +37781,7 @@ class document_reference { } template simdjson_inline simdjson_result get() && -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept @@ -36000,14 +37803,14 @@ class document_reference { * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. */ template - simdjson_inline error_code get(T &out) & -#if SIMDJSON_SUPPORTS_DESERIALIZATION + simdjson_warn_unused simdjson_inline error_code get(T &out) & +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept #endif { -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS if constexpr (custom_deserializable) { return deserialize(*this, out); } else { @@ -36019,7 +37822,7 @@ class document_reference { static_cast(out); // to get rid of unused errors return UNINITIALIZED; } -#else // SIMDJSON_SUPPORTS_DESERIALIZATION +#else // SIMDJSON_SUPPORTS_CONCEPTS // Unless the simdjson library or the user provides an inline implementation, calling this method should // immediately fail. static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " @@ -36029,12 +37832,17 @@ class document_reference { " You may also add support for custom types, see our documentation."); static_cast(out); // to get rid of unused errors return UNINITIALIZED; -#endif // SIMDJSON_SUPPORTS_DESERIALIZATION +#endif // SIMDJSON_SUPPORTS_CONCEPTS } /** @overload template error_code get(T &out) & noexcept */ template simdjson_inline error_code get(T &out) && noexcept; simdjson_inline simdjson_result raw_json() noexcept; +#if SIMDJSON_STATIC_REFLECTION + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept; +#endif // SIMDJSON_STATIC_REFLECTION simdjson_inline operator document&() const noexcept; #if SIMDJSON_EXCEPTIONS template @@ -36103,7 +37911,7 @@ struct simdjson_result : public arm64::implementation simdjson_inline simdjson_result get_double_in_string() noexcept; simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; simdjson_inline simdjson_result get_wobbly_string() noexcept; simdjson_inline simdjson_result get_raw_json_string() noexcept; simdjson_inline simdjson_result get_bool() noexcept; @@ -36116,6 +37924,9 @@ struct simdjson_result : public arm64::implementation template simdjson_inline error_code get(T &out) & noexcept; template simdjson_inline error_code get(T &out) && noexcept; #if SIMDJSON_EXCEPTIONS + + using arm64::implementation_simdjson_result_base::operator*; + using arm64::implementation_simdjson_result_base::operator->; template ::value == false>::type> explicit simdjson_inline operator T() noexcept(false); simdjson_inline operator arm64::ondemand::array() & noexcept(false); @@ -36155,6 +37966,11 @@ struct simdjson_result : public arm64::implementation simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; +#if SIMDJSON_STATIC_REFLECTION + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept; +#endif // SIMDJSON_STATIC_REFLECTION }; @@ -36181,7 +37997,7 @@ struct simdjson_result : public arm64::impl simdjson_inline simdjson_result get_double_in_string() noexcept; simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; simdjson_inline simdjson_result get_wobbly_string() noexcept; simdjson_inline simdjson_result get_raw_json_string() noexcept; simdjson_inline simdjson_result get_bool() noexcept; @@ -36232,6 +38048,11 @@ struct simdjson_result : public arm64::impl simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; +#if SIMDJSON_STATIC_REFLECTION + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept; +#endif // SIMDJSON_STATIC_REFLECTION }; @@ -36374,6 +38195,7 @@ class document_stream { * Default constructor. */ simdjson_inline iterator() noexcept; + simdjson_inline iterator(const iterator &other) noexcept = default; /** * Get the current document (or error). */ @@ -36387,6 +38209,7 @@ class document_stream { * @param other the end iterator to compare to. */ simdjson_inline bool operator!=(const iterator &other) const noexcept; + simdjson_inline bool operator==(const iterator &other) const noexcept; /** * @private * @@ -36430,6 +38253,11 @@ class document_stream { */ inline error_code error() const noexcept; + /** + * Returns whether the iterator is at the end. + */ + inline bool at_end() const noexcept; + private: simdjson_inline iterator(document_stream *s, bool finished) noexcept; /** The document_stream we're iterating through. */ @@ -36441,6 +38269,7 @@ class document_stream { friend class document_stream; friend class json_iterator; }; + using iterator = document_stream::iterator; /** * Start iterating the documents in the stream. @@ -36704,6 +38533,9 @@ struct simdjson_result : public arm64::implementation_si /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */ +/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION && SIMDJSON_SUPPORTS_CONCEPTS */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_string_builder.h" // for constevalutil::fixed_string */ +/* amalgamation skipped (editor-only): #endif */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { @@ -36901,11 +38733,71 @@ class object { */ simdjson_inline simdjson_result raw_json() noexcept; +#if SIMDJSON_SUPPORTS_CONCEPTS + /** + * Get this object as the given type. + * + * @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized. + * @returns INCORRECT_TYPE If the JSON object is not of the given type. + * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. + */ + template + simdjson_warn_unused simdjson_inline error_code get(T &out) + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) { + static_assert(custom_deserializable); + return deserialize(*this, out); + } + /** + * Get this array as the given type. + * + * @returns A value of the given type, parsed from the JSON. + * @returns INCORRECT_TYPE If the JSON value is not the given type. + */ + template + simdjson_inline simdjson_result get() + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) + { + static_assert(std::is_default_constructible::value, "The specified type is not default constructible."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } + +#if SIMDJSON_STATIC_REFLECTION + /** + * Extract only specific fields from the JSON object into a struct. + * + * This allows selective deserialization of only the fields you need, + * potentially improving performance by skipping unwanted fields. + * + * Example: + * ```c++ + * struct Car { + * std::string make; + * std::string model; + * int year; + * double price; + * }; + * + * Car car; + * object.extract_into<"make", "model">(car); + * // Only 'make' and 'model' fields are extracted from JSON + * ``` + * + * @tparam FieldNames Compile-time string literals specifying which fields to extract + * @param out The output struct to populate with selected fields + * @returns SUCCESS on success, or an error code if a required field is missing or has wrong type + */ + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept; +#endif // SIMDJSON_STATIC_REFLECTION +#endif // SIMDJSON_SUPPORTS_CONCEPTS protected: /** * Go to the end of the object, no matter where you are right now. */ - simdjson_inline error_code consume() noexcept; + simdjson_warn_unused simdjson_inline error_code consume() noexcept; static simdjson_inline simdjson_result start(value_iterator &iter) noexcept; static simdjson_inline simdjson_result start_root(value_iterator &iter) noexcept; static simdjson_inline simdjson_result started(value_iterator &iter) noexcept; @@ -36944,12 +38836,42 @@ struct simdjson_result : public arm64::implementation_s simdjson_inline simdjson_result operator[](std::string_view key) && noexcept; simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; - inline simdjson_result reset() noexcept; inline simdjson_result is_empty() noexcept; inline simdjson_result count_fields() & noexcept; inline simdjson_result raw_json() noexcept; + #if SIMDJSON_SUPPORTS_CONCEPTS + // TODO: move this code into object-inl.h + + template + simdjson_inline simdjson_result get() noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + return first; + } + return first.get(); + } + template + simdjson_warn_unused simdjson_inline error_code get(T& out) noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + out = first; + } else { + SIMDJSON_TRY( first.get(out) ); + } + return SUCCESS; + } +#if SIMDJSON_STATIC_REFLECTION + // TODO: move this code into object-inl.h + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) noexcept { + if (error()) { return error(); } + return first.extract_into(out); + } +#endif // SIMDJSON_STATIC_REFLECTION +#endif // SIMDJSON_SUPPORTS_CONCEPTS }; } // namespace simdjson @@ -37078,6 +39000,20 @@ inline simdjson_result to_json_string(simdjson_result to_json_string(simdjson_result x); inline simdjson_result to_json_string(simdjson_result x); inline simdjson_result to_json_string(simdjson_result x); + +#if SIMDJSON_STATIC_REFLECTION +/** + * Create a JSON string from any user-defined type using static reflection. + * Only available when SIMDJSON_STATIC_REFLECTION is enabled. + */ +template + requires(!std::same_as && + !std::same_as && + !std::same_as && + !std::same_as) +inline std::string to_json_string(const T& obj); +#endif + } // namespace simdjson /** @@ -37149,28 +39085,30 @@ inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result #include +#if SIMDJSON_STATIC_REFLECTION +#include +// #include // for std::define_static_string - header not available yet +#endif namespace simdjson { -template -constexpr bool require_custom_serialization = false; ////////////////////////////// // Number deserialization ////////////////////////////// template - requires(!require_custom_serialization) error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { using limits = std::numeric_limits; @@ -37184,7 +39122,6 @@ error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { } template - requires(!require_custom_serialization) error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { double x; SIMDJSON_TRY(val.get_double().get(x)); @@ -37193,7 +39130,6 @@ error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { } template - requires(!require_custom_serialization) error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { using limits = std::numeric_limits; @@ -37206,8 +39142,23 @@ error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { return SUCCESS; } +////////////////////////////// +// String deserialization +////////////////////////////// + +// just a character! +error_code tag_invoke(deserialize_tag, auto &val, char &out) noexcept { + std::string_view x; + SIMDJSON_TRY(val.get_string().get(x)); + if(x.size() != 1) { + return INCORRECT_TYPE; + } + out = x[0]; + return SUCCESS; +} + +// any string-like type (can be constructed from std::string_view) template - requires(!require_custom_serialization) error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(std::is_nothrow_constructible_v) { std::string_view str; SIMDJSON_TRY(val.get_string().get(str)); @@ -37224,7 +39175,6 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(std::is_nothr * doc.get>(). */ template - requires(!require_custom_serialization) error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { using value_type = typename std::remove_cvref_t::value_type; static_assert( @@ -37233,9 +39183,13 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { static_assert( std::is_default_constructible_v, "The specified type inside the container must default constructible."); - arm64::ondemand::array arr; - SIMDJSON_TRY(val.get_array().get(arr)); + if constexpr (std::is_same_v, arm64::ondemand::array>) { + arr = val; + } else { + SIMDJSON_TRY(val.get_array().get(arr)); + } + for (auto v : arr) { if constexpr (concepts::returns_reference) { if (auto const err = v.get().get(concepts::emplace_one(out)); @@ -37266,7 +39220,6 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { * string-keyed types. */ template - requires(!require_custom_serialization) error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { using value_type = typename std::remove_cvref_t::mapped_type; static_assert( @@ -37292,7 +39245,45 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { return SUCCESS; } +template +error_code tag_invoke(deserialize_tag, arm64::ondemand::object &obj, T &out) noexcept { + using value_type = typename std::remove_cvref_t::mapped_type; + + out.clear(); + for (auto field : obj) { + std::string_view key; + SIMDJSON_TRY(field.unescaped_key().get(key)); + + arm64::ondemand::value value_obj; + SIMDJSON_TRY(field.value().get(value_obj)); + + value_type this_value; + SIMDJSON_TRY(value_obj.get(this_value)); + out.emplace(typename T::key_type(key), std::move(this_value)); + } + return SUCCESS; +} + +template +error_code tag_invoke(deserialize_tag, arm64::ondemand::value &val, T &out) noexcept { + arm64::ondemand::object obj; + SIMDJSON_TRY(val.get_object().get(obj)); + return simdjson::deserialize(obj, out); +} + +template +error_code tag_invoke(deserialize_tag, arm64::ondemand::document &doc, T &out) noexcept { + arm64::ondemand::object obj; + SIMDJSON_TRY(doc.get_object().get(obj)); + return simdjson::deserialize(obj, out); +} +template +error_code tag_invoke(deserialize_tag, arm64::ondemand::document_reference &doc, T &out) noexcept { + arm64::ondemand::object obj; + SIMDJSON_TRY(doc.get_object().get(obj)); + return simdjson::deserialize(obj, out); +} /** @@ -37310,7 +39301,6 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { * @return status of the conversion */ template - requires(!require_custom_serialization) error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deserializable::element_type, ValT>) { using element_type = typename std::remove_cvref_t::element_type; @@ -37335,17 +39325,17 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deser /** * This CPO (Customization Point Object) will help deserialize into optional types. */ -template - requires(!require_custom_serialization) -error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deserializable::value_type, ValT>) { +template +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept(nothrow_deserializable::value_type, decltype(val)>) { using value_type = typename std::remove_cvref_t::value_type; - static_assert( - deserializable, - "The specified type inside the unique_ptr must itself be deserializable"); - static_assert( - std::is_default_constructible_v, - "The specified type inside the unique_ptr must default constructible."); + // Check if the value is null + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); // Set to nullopt + return SUCCESS; + } if (!out) { out.emplace(); @@ -37354,10 +39344,329 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deser return SUCCESS; } + +#if SIMDJSON_STATIC_REFLECTION + + +template +constexpr bool user_defined_type = (std::is_class_v +&& !std::is_same_v && !std::is_same_v && !concepts::optional_type && +!concepts::appendable_containers); + + +template + requires(user_defined_type && std::is_class_v) +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept { + arm64::ondemand::object obj; + if constexpr (std::is_same_v, arm64::ondemand::object>) { + obj = val; + } else { + SIMDJSON_TRY(val.get_object().get(obj)); + } + template for (constexpr auto mem : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) { + if constexpr (!std::meta::is_const(mem) && std::meta::is_public(mem)) { + constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem)); + if constexpr (concepts::optional_type) { + // for optional members, it's ok if the key is missing + auto error = obj[key].get(out.[:mem:]); + if (error && error != NO_SUCH_FIELD) { + if(error == NO_SUCH_FIELD) { + out.[:mem:].reset(); + continue; + } + return error; + } + } else { + // for non-optional members, the key must be present + SIMDJSON_TRY(obj[key].get(out.[:mem:])); + } + } + }; + return simdjson::SUCCESS; +} + +// Support for enum deserialization - deserialize from string representation using expand approach from P2996R12 +template + requires(std::is_enum_v) +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept { +#if SIMDJSON_STATIC_REFLECTION + std::string_view str; + SIMDJSON_TRY(val.get_string().get(str)); + constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T)); + template for (constexpr auto enum_val : enumerators) { + if (str == std::meta::identifier_of(enum_val)) { + out = [:enum_val:]; + return SUCCESS; + } + }; + + return INCORRECT_TYPE; +#else + // Fallback: deserialize as integer if reflection not available + std::underlying_type_t int_val; + SIMDJSON_TRY(val.get(int_val)); + out = static_cast(int_val); + return SUCCESS; +#endif +} + +template + requires(user_defined_type>) +error_code tag_invoke(deserialize_tag, simdjson_value &val, std::unique_ptr &out) noexcept { + if (!out) { + out = std::make_unique(); + if (!out) { + return MEMALLOC; + } + } + if (auto err = val.get(*out)) { + out.reset(); + return err; + } + return SUCCESS; +} + +template + requires(user_defined_type>) +error_code tag_invoke(deserialize_tag, simdjson_value &val, std::shared_ptr &out) noexcept { + if (!out) { + out = std::make_shared(); + if (!out) { + return MEMALLOC; + } + } + if (auto err = val.get(*out)) { + out.reset(); + return err; + } + return SUCCESS; +} + +#endif // SIMDJSON_STATIC_REFLECTION + +//////////////////////////////////////// +// Unique pointers +//////////////////////////////////////// +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_unique(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_bool().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_unique(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_int64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_unique(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_uint64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_unique(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_double().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_unique(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_string().get(*out)); + return SUCCESS; +} + + +//////////////////////////////////////// +// Shared pointers +//////////////////////////////////////// +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_shared(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_bool().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_shared(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_int64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_shared(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_uint64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_shared(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_double().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_shared(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_string().get(*out)); + return SUCCESS; +} + + +//////////////////////////////////////// +// Explicit optional specializations +//////////////////////////////////////// + +//////////////////////////////////////// +// Explicit smart pointer specializations for string and int types +//////////////////////////////////////// +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + // Check if the value is null + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); // Set to nullptr + return SUCCESS; + } + + if (!out) { + out = std::make_unique(); + } + std::string_view str; + SIMDJSON_TRY(val.get_string().get(str)); + *out = std::string{str}; + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + // Check if the value is null + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); // Set to nullptr + return SUCCESS; + } + + if (!out) { + out = std::make_shared(); + } + std::string_view str; + SIMDJSON_TRY(val.get_string().get(str)); + *out = std::string{str}; + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + // Check if the value is null + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); // Set to nullptr + return SUCCESS; + } + + if (!out) { + out = std::make_unique(); + } + int64_t temp; + SIMDJSON_TRY(val.get_int64().get(temp)); + *out = static_cast(temp); + return SUCCESS; +} + } // namespace simdjson #endif // SIMDJSON_ONDEMAND_DESERIALIZE_H -#endif // SIMDJSON_SUPPORTS_DESERIALIZATION +#endif // SIMDJSON_SUPPORTS_CONCEPTS /* end file simdjson/generic/ondemand/std_deserialize.h for arm64 */ // Inline definitions @@ -37450,7 +39759,7 @@ simdjson_inline simdjson_result array::begin() noexcept { simdjson_inline simdjson_result array::end() noexcept { return array_iterator(iter); } -simdjson_inline error_code array::consume() noexcept { +simdjson_warn_unused simdjson_warn_unused simdjson_inline error_code array::consume() noexcept { auto error = iter.json_iter().skip_child(iter.depth()-1); if(error) { iter.abandon(); } return error; @@ -37641,6 +39950,9 @@ simdjson_inline array_iterator &array_iterator::operator++() noexcept { return *this; } +simdjson_inline bool array_iterator::at_end() const noexcept { + return iter.at_end(); +} } // namespace ondemand } // namespace arm64 } // namespace simdjson @@ -37677,7 +39989,9 @@ simdjson_inline simdjson_result &simdjson_resul ++(first); return *this; } - +simdjson_inline bool simdjson_result::at_end() const noexcept { + return !first.iter.is_valid() || first.at_end(); +} } // namespace simdjson #endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_INL_H @@ -37734,7 +40048,7 @@ simdjson_inline simdjson_result value::get_string(bool allow_r return iter.get_string(allow_replacement); } template -simdjson_inline error_code value::get_string(string_type& receiver, bool allow_replacement) noexcept { +simdjson_warn_unused simdjson_inline error_code value::get_string(string_type& receiver, bool allow_replacement) noexcept { return iter.get_string(receiver, allow_replacement); } simdjson_inline simdjson_result value::get_wobbly_string() noexcept { @@ -37776,15 +40090,15 @@ template<> simdjson_inline simdjson_result value::get() noexcept { retu template<> simdjson_inline simdjson_result value::get() noexcept { return get_bool(); } -template<> simdjson_inline error_code value::get(array& out) noexcept { return get_array().get(out); } -template<> simdjson_inline error_code value::get(object& out) noexcept { return get_object().get(out); } -template<> simdjson_inline error_code value::get(raw_json_string& out) noexcept { return get_raw_json_string().get(out); } -template<> simdjson_inline error_code value::get(std::string_view& out) noexcept { return get_string(false).get(out); } -template<> simdjson_inline error_code value::get(number& out) noexcept { return get_number().get(out); } -template<> simdjson_inline error_code value::get(double& out) noexcept { return get_double().get(out); } -template<> simdjson_inline error_code value::get(uint64_t& out) noexcept { return get_uint64().get(out); } -template<> simdjson_inline error_code value::get(int64_t& out) noexcept { return get_int64().get(out); } -template<> simdjson_inline error_code value::get(bool& out) noexcept { return get_bool().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(array& out) noexcept { return get_array().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(object& out) noexcept { return get_object().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(raw_json_string& out) noexcept { return get_raw_json_string().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(std::string_view& out) noexcept { return get_string(false).get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(number& out) noexcept { return get_number().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(double& out) noexcept { return get_double().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(uint64_t& out) noexcept { return get_uint64().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(int64_t& out) noexcept { return get_int64().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(bool& out) noexcept { return get_bool().get(out); } #if SIMDJSON_EXCEPTIONS template @@ -38381,7 +40695,7 @@ simdjson_inline simdjson_result document::get_string(bool allo return get_root_value_iterator().get_root_string(true, allow_replacement); } template -simdjson_inline error_code document::get_string(string_type& receiver, bool allow_replacement) noexcept { +simdjson_warn_unused simdjson_inline error_code document::get_string(string_type& receiver, bool allow_replacement) noexcept { return get_root_value_iterator().get_root_string(receiver, true, allow_replacement); } simdjson_inline simdjson_result document::get_wobbly_string() noexcept { @@ -38407,15 +40721,15 @@ template<> simdjson_inline simdjson_result document::get() & noexcept { template<> simdjson_inline simdjson_result document::get() & noexcept { return get_bool(); } template<> simdjson_inline simdjson_result document::get() & noexcept { return get_value(); } -template<> simdjson_inline error_code document::get(array& out) & noexcept { return get_array().get(out); } -template<> simdjson_inline error_code document::get(object& out) & noexcept { return get_object().get(out); } -template<> simdjson_inline error_code document::get(raw_json_string& out) & noexcept { return get_raw_json_string().get(out); } -template<> simdjson_inline error_code document::get(std::string_view& out) & noexcept { return get_string(false).get(out); } -template<> simdjson_inline error_code document::get(double& out) & noexcept { return get_double().get(out); } -template<> simdjson_inline error_code document::get(uint64_t& out) & noexcept { return get_uint64().get(out); } -template<> simdjson_inline error_code document::get(int64_t& out) & noexcept { return get_int64().get(out); } -template<> simdjson_inline error_code document::get(bool& out) & noexcept { return get_bool().get(out); } -template<> simdjson_inline error_code document::get(value& out) & noexcept { return get_value().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(array& out) & noexcept { return get_array().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(object& out) & noexcept { return get_object().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(raw_json_string& out) & noexcept { return get_raw_json_string().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(std::string_view& out) & noexcept { return get_string(false).get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(double& out) & noexcept { return get_double().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(uint64_t& out) & noexcept { return get_uint64().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(int64_t& out) & noexcept { return get_int64().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(bool& out) & noexcept { return get_bool().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(value& out) & noexcept { return get_value().get(out); } template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_raw_json_string(); } template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_string(false); } @@ -38435,8 +40749,8 @@ simdjson_inline document::operator object() & noexcept(false) { return get_objec simdjson_inline document::operator uint64_t() noexcept(false) { return get_uint64(); } simdjson_inline document::operator int64_t() noexcept(false) { return get_int64(); } simdjson_inline document::operator double() noexcept(false) { return get_double(); } -simdjson_inline document::operator std::string_view() noexcept(false) { return get_string(false); } -simdjson_inline document::operator raw_json_string() noexcept(false) { return get_raw_json_string(); } +simdjson_inline document::operator std::string_view() noexcept(false) simdjson_lifetime_bound { return get_string(false); } +simdjson_inline document::operator raw_json_string() noexcept(false) simdjson_lifetime_bound { return get_raw_json_string(); } simdjson_inline document::operator bool() noexcept(false) { return get_bool(); } simdjson_inline document::operator value() noexcept(false) { return get_value(); } @@ -38485,7 +40799,7 @@ simdjson_inline simdjson_result document::operator[](const char *key) & n return start_or_resume_object()[key]; } -simdjson_inline error_code document::consume() noexcept { +simdjson_warn_unused simdjson_inline error_code document::consume() noexcept { bool scalar = false; auto error = is_scalar().get(scalar); if(error) { return error; } @@ -38587,6 +40901,54 @@ simdjson_inline simdjson_result document::at_path(std::string_view json_p } } + + +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION + +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code document::extract_into(T& out) & noexcept { + // Helper to check if a field name matches any of the requested fields + auto should_extract = [](std::string_view field_name) constexpr -> bool { + return ((FieldNames.view() == field_name) || ...); + }; + + // Iterate through all members of T using reflection + template for (constexpr auto mem : std::define_static_array( + std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) { + + if constexpr (!std::meta::is_const(mem) && std::meta::is_public(mem)) { + constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem)); + + // Only extract this field if it's in our list of requested fields + if constexpr (should_extract(key)) { + // Try to find and extract the field + if constexpr (concepts::optional_type) { + // For optional fields, it's ok if they're missing + auto field_result = find_field_unordered(key); + if (!field_result.error()) { + auto error = field_result.get(out.[:mem:]); + if (error && error != NO_SUCH_FIELD) { + return error; + } + } else if (field_result.error() != NO_SUCH_FIELD) { + return field_result.error(); + } else { + out.[:mem:].reset(); + } + } else { + // For required fields (in the requested list), fail if missing + SIMDJSON_TRY((*this)[key].get(out.[:mem:])); + } + } + } + }; + + return SUCCESS; +} + +#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION + } // namespace ondemand } // namespace arm64 } // namespace simdjson @@ -38694,7 +41056,7 @@ simdjson_inline simdjson_result simdjson_result -simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { if (error()) { return error(); } return first.get_string(receiver, allow_replacement); } @@ -38730,12 +41092,12 @@ simdjson_deprecated simdjson_inline simdjson_result simdjson_result(first).get(); } template -simdjson_inline error_code simdjson_result::get(T &out) & noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) & noexcept { if (error()) { return error(); } return first.get(out); } template -simdjson_inline error_code simdjson_result::get(T &out) && noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) && noexcept { if (error()) { return error(); } return std::forward(first).get(out); } @@ -38745,8 +41107,8 @@ template<> simdjson_deprecated simdjson_inline simdjson_result(first); } -template<> simdjson_inline error_code simdjson_result::get(arm64::ondemand::document &out) & noexcept = delete; -template<> simdjson_inline error_code simdjson_result::get(arm64::ondemand::document &out) && noexcept { +template<> simdjson_warn_unused simdjson_inline error_code simdjson_result::get(arm64::ondemand::document &out) & noexcept = delete; +template<> simdjson_warn_unused simdjson_inline error_code simdjson_result::get(arm64::ondemand::document &out) && noexcept { if (error()) { return error(); } out = std::forward(first); return SUCCESS; @@ -38864,6 +41226,15 @@ simdjson_inline simdjson_result simdjson_result + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code simdjson_result::extract_into(T& out) & noexcept { + if (error()) { return error(); } + return first.extract_into(out); +} +#endif // SIMDJSON_STATIC_REFLECTION + } // namespace simdjson @@ -38898,7 +41269,7 @@ simdjson_inline simdjson_result document_reference::get_double() noexcep simdjson_inline simdjson_result document_reference::get_double_in_string() noexcept { return doc->get_root_value_iterator().get_root_double(false); } simdjson_inline simdjson_result document_reference::get_string(bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(false, allow_replacement); } template -simdjson_inline error_code document_reference::get_string(string_type& receiver, bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(receiver, false, allow_replacement); } +simdjson_warn_unused simdjson_inline error_code document_reference::get_string(string_type& receiver, bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(receiver, false, allow_replacement); } simdjson_inline simdjson_result document_reference::get_wobbly_string() noexcept { return doc->get_root_value_iterator().get_root_wobbly_string(false); } simdjson_inline simdjson_result document_reference::get_raw_json_string() noexcept { return doc->get_root_value_iterator().get_root_raw_json_string(false); } simdjson_inline simdjson_result document_reference::get_bool() noexcept { return doc->get_root_value_iterator().get_root_bool(false); } @@ -38951,7 +41322,13 @@ simdjson_inline simdjson_result document_reference::at_pointer(std::strin simdjson_inline simdjson_result document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); } simdjson_inline simdjson_result document_reference::raw_json() noexcept { return doc->raw_json();} simdjson_inline document_reference::operator document&() const noexcept { return *doc; } - +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code document_reference::extract_into(T& out) & noexcept { + return doc->extract_into(out); +} +#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION } // namespace ondemand } // namespace arm64 } // namespace simdjson @@ -39048,7 +41425,7 @@ simdjson_inline simdjson_result simdjson_result -simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { if (error()) { return error(); } return first.get_string(receiver, allow_replacement); } @@ -39083,12 +41460,12 @@ simdjson_inline simdjson_result simdjson_result(first).get(); } template -simdjson_inline error_code simdjson_result::get(T &out) & noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) & noexcept { if (error()) { return error(); } return first.get(out); } template -simdjson_inline error_code simdjson_result::get(T &out) && noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) && noexcept { if (error()) { return error(); } return std::forward(first).get(out); } @@ -39105,13 +41482,13 @@ simdjson_inline simdjson_result simdjson_result -simdjson_inline error_code simdjson_result::get(arm64::ondemand::document_reference &out) & noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(arm64::ondemand::document_reference &out) & noexcept { if (error()) { return error(); } out = first; return SUCCESS; } template <> -simdjson_inline error_code simdjson_result::get(arm64::ondemand::document_reference &out) && noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(arm64::ondemand::document_reference &out) && noexcept { if (error()) { return error(); } out = first; return SUCCESS; @@ -39199,7 +41576,14 @@ simdjson_inline simdjson_result simdjson_result + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code simdjson_result::extract_into(T& out) & noexcept { + if (error()) { return error(); } + return first.extract_into(out); +} +#endif // SIMDJSON_STATIC_REFLECTION } // namespace simdjson #endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H @@ -39390,10 +41774,19 @@ simdjson_inline document_stream::iterator& document_stream::iterator::operator++ return *this; } +simdjson_inline bool document_stream::iterator::at_end() const noexcept { + return finished; +} + + simdjson_inline bool document_stream::iterator::operator!=(const document_stream::iterator &other) const noexcept { return finished != other.finished; } +simdjson_inline bool document_stream::iterator::operator==(const document_stream::iterator &other) const noexcept { + return finished == other.finished; +} + simdjson_inline document_stream::iterator document_stream::begin() noexcept { start(); // If there are no documents, we're finished. @@ -39511,7 +41904,10 @@ inline void document_stream::next_document() noexcept { // Always set depth=1 at the start of document doc.iter._depth = 1; // consume comma if comma separated is allowed - if (allow_comma_separated) { doc.iter.consume_character(','); } + if (allow_comma_separated) { + error_code ignored = doc.iter.consume_character(','); + static_cast(ignored); // ignored on purpose + } // Resets the string buffer at the beginning, thus invalidating the strings. doc.iter._string_buf_loc = parser->string_buf.get(); doc.iter._root = doc.iter.position(); @@ -39757,7 +42153,7 @@ simdjson_inline simdjson_result simdjson_result -simdjson_inline error_code simdjson_result::unescaped_key(string_type &receiver, bool allow_replacement) noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::unescaped_key(string_type &receiver, bool allow_replacement) noexcept { if (error()) { return error(); } return first.unescaped_key(receiver, allow_replacement); } @@ -39993,6 +42389,8 @@ simdjson_inline void json_iterator::assert_valid_position(token_position positio #ifndef SIMDJSON_CLANG_VISUAL_STUDIO SIMDJSON_ASSUME( position >= &parser->implementation->structural_indexes[0] ); SIMDJSON_ASSUME( position < &parser->implementation->structural_indexes[parser->implementation->n_structural_indexes] ); +#else + (void)position; // Suppress unused parameter warning #endif } @@ -40117,7 +42515,7 @@ simdjson_inline uint8_t *&json_iterator::string_buf_loc() noexcept { return _string_buf_loc; } -simdjson_inline error_code json_iterator::report_error(error_code _error, const char *message) noexcept { +simdjson_warn_unused simdjson_inline error_code json_iterator::report_error(error_code _error, const char *message) noexcept { SIMDJSON_ASSUME(_error != SUCCESS && _error != UNINITIALIZED && _error != INCORRECT_TYPE && _error != NO_SUCH_FIELD); logger::log_error(*this, message); error = _error; @@ -40161,7 +42559,7 @@ simdjson_inline void json_iterator::reenter_child(token_position position, depth _depth = child_depth; } -simdjson_inline error_code json_iterator::consume_character(char c) noexcept { +simdjson_warn_unused simdjson_inline error_code json_iterator::consume_character(char c) noexcept { if (*peek() == c) { return_current_and_advance(); return SUCCESS; @@ -40184,7 +42582,7 @@ simdjson_inline void json_iterator::set_start_position(depth_t depth, token_posi #endif -simdjson_inline error_code json_iterator::optional_error(error_code _error, const char *message) noexcept { +simdjson_warn_unused simdjson_inline error_code json_iterator::optional_error(error_code _error, const char *message) noexcept { SIMDJSON_ASSUME(_error == INCORRECT_TYPE || _error == NO_SUCH_FIELD); logger::log_error(*this, message); return _error; @@ -40579,6 +42977,10 @@ inline void log_line(const json_iterator &iter, token_position index, depth_t de /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value-inl.h" */ +/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_string_builder.h" // for constevalutil::fixed_string */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #endif */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { @@ -40636,7 +43038,7 @@ simdjson_inline simdjson_result object::start_root(value_iterator &iter) SIMDJSON_TRY( iter.start_root_object().error() ); return object(iter); } -simdjson_inline error_code object::consume() noexcept { +simdjson_warn_unused simdjson_inline error_code object::consume() noexcept { if(iter.is_at_key()) { /** * whenever you are pointing at a key, calling skip_child() is @@ -40765,6 +43167,52 @@ simdjson_inline simdjson_result object::reset() & noexcept { return iter.reset_object(); } +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION + +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code object::extract_into(T& out) & noexcept { + // Helper to check if a field name matches any of the requested fields + auto should_extract = [](std::string_view field_name) constexpr -> bool { + return ((FieldNames.view() == field_name) || ...); + }; + + // Iterate through all members of T using reflection + template for (constexpr auto mem : std::define_static_array( + std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) { + + if constexpr (!std::meta::is_const(mem) && std::meta::is_public(mem)) { + constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem)); + + // Only extract this field if it's in our list of requested fields + if constexpr (should_extract(key)) { + // Try to find and extract the field + if constexpr (concepts::optional_type) { + // For optional fields, it's ok if they're missing + auto field_result = find_field_unordered(key); + if (!field_result.error()) { + auto error = field_result.get(out.[:mem:]); + if (error && error != NO_SUCH_FIELD) { + return error; + } + } else if (field_result.error() != NO_SUCH_FIELD) { + return field_result.error(); + } else { + out.[:mem:].reset(); + } + } else { + // For required fields (in the requested list), fail if missing + SIMDJSON_TRY((*this)[key].get(out.[:mem:])); + } + } + } + }; + + return SUCCESS; +} + +#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION + } // namespace ondemand } // namespace arm64 } // namespace simdjson @@ -40841,6 +43289,7 @@ simdjson_inline simdjson_result simdjson_result parser::iterate(padded_string_view json) & noexcept { - if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; } + if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; } json.remove_utf8_bom(); @@ -41055,7 +43504,7 @@ simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(p #ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON simdjson_warn_unused simdjson_inline simdjson_result parser::iterate_allow_incomplete_json(padded_string_view json) & noexcept { - if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; } + if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; } json.remove_utf8_bom(); @@ -41087,10 +43536,7 @@ simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(s } simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(std::string &json) & noexcept { - if(json.capacity() - json.size() < SIMDJSON_PADDING) { - json.reserve(json.size() + SIMDJSON_PADDING); - } - return iterate(padded_string_view(json)); + return iterate(pad_with_reserve(json)); } simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(const std::string &json) & noexcept { @@ -41112,7 +43558,7 @@ simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(c } simdjson_warn_unused simdjson_inline simdjson_result parser::iterate_raw(padded_string_view json) & noexcept { - if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; } + if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; } json.remove_utf8_bom(); @@ -41127,6 +43573,7 @@ simdjson_warn_unused simdjson_inline simdjson_result parser::iter } inline simdjson_result parser::iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept { + // Warning: no check is done on the buffer padding. We trust the user. if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; } if((len >= 3) && (std::memcmp(buf, "\xEF\xBB\xBF", 3) == 0)) { buf += 3; @@ -41135,16 +43582,24 @@ inline simdjson_result parser::iterate_many(const uint8_t *buf, if(allow_comma_separated && batch_size < len) { batch_size = len; } return document_stream(*this, buf, len, batch_size, allow_comma_separated); } + inline simdjson_result parser::iterate_many(const char *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept { + // Warning: no check is done on the buffer padding. We trust the user. return iterate_many(reinterpret_cast(buf), len, batch_size, allow_comma_separated); } -inline simdjson_result parser::iterate_many(const std::string &s, size_t batch_size, bool allow_comma_separated) noexcept { +inline simdjson_result parser::iterate_many(padded_string_view s, size_t batch_size, bool allow_comma_separated) noexcept { + if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; } return iterate_many(s.data(), s.length(), batch_size, allow_comma_separated); } inline simdjson_result parser::iterate_many(const padded_string &s, size_t batch_size, bool allow_comma_separated) noexcept { - return iterate_many(s.data(), s.length(), batch_size, allow_comma_separated); + return iterate_many(padded_string_view(s), batch_size, allow_comma_separated); +} +inline simdjson_result parser::iterate_many(const std::string &s, size_t batch_size, bool allow_comma_separated) noexcept { + return iterate_many(padded_string_view(s), batch_size, allow_comma_separated); +} +inline simdjson_result parser::iterate_many(std::string &s, size_t batch_size, bool allow_comma_separated) noexcept { + return iterate_many(pad(s), batch_size, allow_comma_separated); } - simdjson_pure simdjson_inline size_t parser::capacity() const noexcept { return _capacity; } @@ -41179,6 +43634,34 @@ simdjson_inline simdjson_warn_unused simdjson_result parser::u return result; } +simdjson_inline simdjson_warn_unused ondemand::parser& parser::get_parser() { + return *parser::get_parser_instance(); +} + +simdjson_inline bool release_parser() { + auto &parser_instance = parser::get_threadlocal_parser_if_exists(); + if (parser_instance) { + parser_instance.reset(); + return true; + } + return false; +} + +simdjson_inline simdjson_warn_unused std::unique_ptr& parser::get_parser_instance() { + std::unique_ptr& parser_instance = get_threadlocal_parser_if_exists(); + if (!parser_instance) { + parser_instance.reset(new ondemand::parser()); + } + return parser_instance; +} + +simdjson_inline simdjson_warn_unused std::unique_ptr& parser::get_threadlocal_parser_if_exists() { + // @the-moisrex points out that this could be implemented with std::optional (C++17). + thread_local std::unique_ptr parser_instance = nullptr; + return parser_instance; +} + + } // namespace ondemand } // namespace arm64 } // namespace simdjson @@ -41213,8 +43696,13 @@ namespace ondemand { simdjson_inline raw_json_string::raw_json_string(const uint8_t * _buf) noexcept : buf{_buf} {} -simdjson_inline const char * raw_json_string::raw() const noexcept { return reinterpret_cast(buf); } +simdjson_inline const char * raw_json_string::raw() const noexcept { + return reinterpret_cast(buf); +} +simdjson_inline char raw_json_string::operator[](size_t i) const noexcept { + return reinterpret_cast(buf)[i]; +} simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(std::string_view target) noexcept { size_t pos{0}; @@ -41391,6 +43879,10 @@ simdjson_inline simdjson_result simdjson_result::operator[](size_t i) const noexcept { + if (error()) { return error(); } + return first[i]; +} simdjson_inline simdjson_warn_unused simdjson_result simdjson_result::unescape(arm64::ondemand::json_iterator &iter, bool allow_replacement) const noexcept { if (error()) { return error(); } return first.unescape(iter, allow_replacement); @@ -41416,6 +43908,9 @@ simdjson_inline simdjson_warn_unused simdjson_result simdjson_ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/serialization.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */ +/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_builder.h" */ +/* amalgamation skipped (editor-only): #endif */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { @@ -41781,7 +44276,7 @@ simdjson_warn_unused simdjson_inline simdjson_result value_iterator::start if (*_json_iter->peek() == '}') { logger::log_value(*_json_iter, "empty object"); _json_iter->return_current_and_advance(); - end_container(); + SIMDJSON_TRY(end_container()); return false; } return true; @@ -42635,7 +45130,7 @@ simdjson_inline void value_iterator::advance_scalar(const char *type) noexcept { _json_iter->ascend_to(depth()-1); } -simdjson_inline error_code value_iterator::start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept { +simdjson_warn_unused simdjson_inline error_code value_iterator::start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept { logger::log_start_value(*_json_iter, start_position(), depth(), type); // If we're not at the position anymore, we don't want to advance the cursor. const uint8_t *json; @@ -42797,7 +45292,7 @@ simdjson_inline simdjson_result value_iterator::type() const noexcept case '5': case '6': case '7': case '8': case '9': return json_type::number; default: - return TAPE_ERROR; + return json_type::unknown; } } @@ -42837,6 +45332,1097 @@ simdjson_inline simdjson_result::simdjson_resul #endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H /* end file simdjson/generic/ondemand/value_iterator-inl.h for arm64 */ +// JSON builder inline definitions +/* including simdjson/generic/ondemand/json_string_builder-inl.h for arm64: #include "simdjson/generic/ondemand/json_string_builder-inl.h" */ +/* begin file simdjson/generic/ondemand/json_string_builder-inl.h for arm64 */ +/** + * This file is part of the builder API. It is temporarily in the ondemand + * directory but we will move it to a builder directory later. + */ +#include +#include +#include +#ifndef SIMDJSON_GENERIC_STRING_BUILDER_INL_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_INL_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_string_builder.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +/* + * Empirically, we have found that an inlined optimization is important for + * performance. The following macros are not ideal. We should find a better + * way to inline the code. + */ + +#if defined(__SSE2__) || defined(__x86_64__) || defined(__x86_64) || \ + (defined(_M_AMD64) || defined(_M_X64) || \ + (defined(_M_IX86_FP) && _M_IX86_FP == 2)) +#ifndef SIMDJSON_EXPERIMENTAL_HAS_SSE2 +#define SIMDJSON_EXPERIMENTAL_HAS_SSE2 1 +#endif +#endif + +#if defined(__aarch64__) || defined(_M_ARM64) +#ifndef SIMDJSON_EXPERIMENTAL_HAS_NEON +#define SIMDJSON_EXPERIMENTAL_HAS_NEON 1 +#endif +#endif +#if SIMDJSON_EXPERIMENTAL_HAS_NEON +#include +#endif +#if SIMDJSON_EXPERIMENTAL_HAS_SSE2 +#include +#endif + +namespace simdjson { +namespace arm64 { +namespace builder { + +static SIMDJSON_CONSTEXPR_LAMBDA std::array + json_quotable_character = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 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, 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, 1, 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, 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, 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, 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, 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, 0, 0, 0}; + +/** + +A possible SWAR implementation of has_json_escapable_byte. It is not used +because it is slower than the current implementation. It is kept here for +reference (to show that we tried it). + +inline bool has_json_escapable_byte(uint64_t x) { + uint64_t is_ascii = 0x8080808080808080ULL & ~x; + uint64_t xor2 = x ^ 0x0202020202020202ULL; + uint64_t lt32_or_eq34 = xor2 - 0x2121212121212121ULL; + uint64_t sub92 = x ^ 0x5C5C5C5C5C5C5C5CULL; + uint64_t eq92 = (sub92 - 0x0101010101010101ULL); + return ((lt32_or_eq34 | eq92) & is_ascii) != 0; +} + +**/ + +SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline bool +simple_needs_escaping(std::string_view v) { + for (char c : v) { + // a table lookup is faster than a series of comparisons + if (json_quotable_character[static_cast(c)]) { + return true; + } + } + return false; +} + +#if SIMDJSON_EXPERIMENTAL_HAS_NEON +simdjson_inline bool fast_needs_escaping(std::string_view view) { + if (view.size() < 16) { + return simple_needs_escaping(view); + } + size_t i = 0; + uint8x16_t running = vdupq_n_u8(0); + uint8x16_t v34 = vdupq_n_u8(34); + uint8x16_t v92 = vdupq_n_u8(92); + + for (; i + 15 < view.size(); i += 16) { + uint8x16_t word = vld1q_u8((const uint8_t *)view.data() + i); + running = vorrq_u8(running, vceqq_u8(word, v34)); + running = vorrq_u8(running, vceqq_u8(word, v92)); + running = vorrq_u8(running, vcltq_u8(word, vdupq_n_u8(32))); + } + if (i < view.size()) { + uint8x16_t word = + vld1q_u8((const uint8_t *)view.data() + view.length() - 16); + running = vorrq_u8(running, vceqq_u8(word, v34)); + running = vorrq_u8(running, vceqq_u8(word, v92)); + running = vorrq_u8(running, vcltq_u8(word, vdupq_n_u8(32))); + } + return vmaxvq_u32(vreinterpretq_u32_u8(running)) != 0; +} +#elif SIMDJSON_EXPERIMENTAL_HAS_SSE2 +simdjson_inline bool fast_needs_escaping(std::string_view view) { + if (view.size() < 16) { + return simple_needs_escaping(view); + } + size_t i = 0; + __m128i running = _mm_setzero_si128(); + for (; i + 15 < view.size(); i += 16) { + + __m128i word = + _mm_loadu_si128(reinterpret_cast(view.data() + i)); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(34))); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(92))); + running = _mm_or_si128( + running, _mm_cmpeq_epi8(_mm_subs_epu8(word, _mm_set1_epi8(31)), + _mm_setzero_si128())); + } + if (i < view.size()) { + __m128i word = _mm_loadu_si128( + reinterpret_cast(view.data() + view.length() - 16)); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(34))); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(92))); + running = _mm_or_si128( + running, _mm_cmpeq_epi8(_mm_subs_epu8(word, _mm_set1_epi8(31)), + _mm_setzero_si128())); + } + return _mm_movemask_epi8(running) != 0; +} +#else +simdjson_inline bool fast_needs_escaping(std::string_view view) { + return simple_needs_escaping(view); +} +#endif + +SIMDJSON_CONSTEXPR_LAMBDA inline size_t +find_next_json_quotable_character(const std::string_view view, + size_t location) noexcept { + + for (auto pos = view.begin() + location; pos != view.end(); ++pos) { + if (json_quotable_character[static_cast(*pos)]) { + return pos - view.begin(); + } + } + return size_t(view.size()); +} + +SIMDJSON_CONSTEXPR_LAMBDA static std::string_view control_chars[] = { + "\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", "\\u0006", + "\\u0007", "\\b", "\\t", "\\n", "\\u000b", "\\f", "\\r", + "\\u000e", "\\u000f", "\\u0010", "\\u0011", "\\u0012", "\\u0013", "\\u0014", + "\\u0015", "\\u0016", "\\u0017", "\\u0018", "\\u0019", "\\u001a", "\\u001b", + "\\u001c", "\\u001d", "\\u001e", "\\u001f"}; + +// All Unicode characters may be placed within the quotation marks, except for +// the characters that MUST be escaped: quotation mark, reverse solidus, and the +// control characters (U+0000 through U+001F). There are two-character sequence +// escape representations of some popular characters: +// \", \\, \b, \f, \n, \r, \t. +SIMDJSON_CONSTEXPR_LAMBDA void escape_json_char(char c, char *&out) { + if (c == '"') { + memcpy(out, "\\\"", 2); + out += 2; + } else if (c == '\\') { + memcpy(out, "\\\\", 2); + out += 2; + } else { + std::string_view v = control_chars[uint8_t(c)]; + memcpy(out, v.data(), v.size()); + out += v.size(); + } +} + +inline size_t write_string_escaped(const std::string_view input, char *out) { + size_t mysize = input.size(); + if (!fast_needs_escaping(input)) { // fast path! + memcpy(out, input.data(), input.size()); + return input.size(); + } + const char *const initout = out; + size_t location = find_next_json_quotable_character(input, 0); + memcpy(out, input.data(), location); + out += location; + escape_json_char(input[location], out); + location += 1; + while (location < mysize) { + size_t newlocation = find_next_json_quotable_character(input, location); + memcpy(out, input.data() + location, newlocation - location); + out += newlocation - location; + location = newlocation; + if (location == mysize) { + break; + } + escape_json_char(input[location], out); + location += 1; + } + return out - initout; +} + +simdjson_inline string_builder::string_builder(size_t initial_capacity) + : buffer(new(std::nothrow) char[initial_capacity]), position(0), + capacity(buffer.get() != nullptr ? initial_capacity : 0), + is_valid(buffer.get() != nullptr) {} + +simdjson_inline bool string_builder::capacity_check(size_t upcoming_bytes) { + // We use the convention that when is_valid is false, then the capacity and + // the position are 0. + // Most of the time, this function will return true. + if (simdjson_likely(upcoming_bytes <= capacity - position)) { + return true; + } + // check for overflow, most of the time there is no overflow + if (simdjson_likely(position + upcoming_bytes < position)) { + return false; + } + // We will rarely get here. + grow_buffer((std::max)(capacity * 2, position + upcoming_bytes)); + // If the buffer allocation failed, we set is_valid to false. + return is_valid; +} + +simdjson_inline void string_builder::grow_buffer(size_t desired_capacity) { + if (!is_valid) { + return; + } + std::unique_ptr new_buffer(new (std::nothrow) char[desired_capacity]); + if (new_buffer.get() == nullptr) { + set_valid(false); + return; + } + std::memcpy(new_buffer.get(), buffer.get(), position); + buffer.swap(new_buffer); + capacity = desired_capacity; +} + +simdjson_inline void string_builder::set_valid(bool valid) noexcept { + if (!valid) { + is_valid = false; + capacity = 0; + position = 0; + buffer.reset(); + } else { + is_valid = true; + } +} + +simdjson_inline size_t string_builder::size() const noexcept { + return position; +} + +simdjson_inline void string_builder::append(char c) noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = c; + } +} + +simdjson_inline void string_builder::append_null() noexcept { + constexpr char null_literal[] = "null"; + constexpr size_t null_len = sizeof(null_literal) - 1; + if (capacity_check(null_len)) { + std::memcpy(buffer.get() + position, null_literal, null_len); + position += null_len; + } +} + +simdjson_inline void string_builder::clear() noexcept { + position = 0; + // if it was invalid, we should try to repair it + if (!is_valid) { + capacity = 0; + buffer.reset(); + is_valid = true; + } +} + +namespace internal { + +template ::value>::type> +simdjson_really_inline int int_log2(number_type x) { + return 63 - leading_zeroes(uint64_t(x) | 1); +} + +simdjson_really_inline int fast_digit_count_32(uint32_t x) { + static uint64_t table[] = { + 4294967296, 8589934582, 8589934582, 8589934582, 12884901788, + 12884901788, 12884901788, 17179868184, 17179868184, 17179868184, + 21474826480, 21474826480, 21474826480, 21474826480, 25769703776, + 25769703776, 25769703776, 30063771072, 30063771072, 30063771072, + 34349738368, 34349738368, 34349738368, 34349738368, 38554705664, + 38554705664, 38554705664, 41949672960, 41949672960, 41949672960, + 42949672960, 42949672960}; + return uint32_t((x + table[int_log2(x)]) >> 32); +} + +simdjson_really_inline int fast_digit_count_64(uint64_t x) { + static uint64_t table[] = {9, + 99, + 999, + 9999, + 99999, + 999999, + 9999999, + 99999999, + 999999999, + 9999999999, + 99999999999, + 999999999999, + 9999999999999, + 99999999999999, + 999999999999999ULL, + 9999999999999999ULL, + 99999999999999999ULL, + 999999999999999999ULL, + 9999999999999999999ULL}; + int y = (19 * int_log2(x) >> 6); + y += x > table[y]; + return y + 1; +} + +template ::value>::type> +simdjson_really_inline size_t digit_count(number_type v) noexcept { + static_assert(sizeof(number_type) == 8 || sizeof(number_type) == 4 || + sizeof(number_type) == 2 || sizeof(number_type) == 1, + "We only support 8-bit, 16-bit, 32-bit and 64-bit numbers"); + SIMDJSON_IF_CONSTEXPR(sizeof(number_type) <= 4) { + return fast_digit_count_32(static_cast(v)); + } + else { + return fast_digit_count_64(static_cast(v)); + } +} +static const char decimal_table[200] = { + 0x30, 0x30, 0x30, 0x31, 0x30, 0x32, 0x30, 0x33, 0x30, 0x34, 0x30, 0x35, + 0x30, 0x36, 0x30, 0x37, 0x30, 0x38, 0x30, 0x39, 0x31, 0x30, 0x31, 0x31, + 0x31, 0x32, 0x31, 0x33, 0x31, 0x34, 0x31, 0x35, 0x31, 0x36, 0x31, 0x37, + 0x31, 0x38, 0x31, 0x39, 0x32, 0x30, 0x32, 0x31, 0x32, 0x32, 0x32, 0x33, + 0x32, 0x34, 0x32, 0x35, 0x32, 0x36, 0x32, 0x37, 0x32, 0x38, 0x32, 0x39, + 0x33, 0x30, 0x33, 0x31, 0x33, 0x32, 0x33, 0x33, 0x33, 0x34, 0x33, 0x35, + 0x33, 0x36, 0x33, 0x37, 0x33, 0x38, 0x33, 0x39, 0x34, 0x30, 0x34, 0x31, + 0x34, 0x32, 0x34, 0x33, 0x34, 0x34, 0x34, 0x35, 0x34, 0x36, 0x34, 0x37, + 0x34, 0x38, 0x34, 0x39, 0x35, 0x30, 0x35, 0x31, 0x35, 0x32, 0x35, 0x33, + 0x35, 0x34, 0x35, 0x35, 0x35, 0x36, 0x35, 0x37, 0x35, 0x38, 0x35, 0x39, + 0x36, 0x30, 0x36, 0x31, 0x36, 0x32, 0x36, 0x33, 0x36, 0x34, 0x36, 0x35, + 0x36, 0x36, 0x36, 0x37, 0x36, 0x38, 0x36, 0x39, 0x37, 0x30, 0x37, 0x31, + 0x37, 0x32, 0x37, 0x33, 0x37, 0x34, 0x37, 0x35, 0x37, 0x36, 0x37, 0x37, + 0x37, 0x38, 0x37, 0x39, 0x38, 0x30, 0x38, 0x31, 0x38, 0x32, 0x38, 0x33, + 0x38, 0x34, 0x38, 0x35, 0x38, 0x36, 0x38, 0x37, 0x38, 0x38, 0x38, 0x39, + 0x39, 0x30, 0x39, 0x31, 0x39, 0x32, 0x39, 0x33, 0x39, 0x34, 0x39, 0x35, + 0x39, 0x36, 0x39, 0x37, 0x39, 0x38, 0x39, 0x39, +}; +} // namespace internal + +template +simdjson_inline void string_builder::append(number_type v) noexcept { + static_assert(std::is_same::value || + std::is_integral::value || + std::is_floating_point::value, + "Unsupported number type"); + // If C++17 is available, we can 'if constexpr' here. + SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + if (v) { + constexpr char true_literal[] = "true"; + constexpr size_t true_len = sizeof(true_literal) - 1; + if (capacity_check(true_len)) { + std::memcpy(buffer.get() + position, true_literal, true_len); + position += true_len; + } + } else { + constexpr char false_literal[] = "false"; + constexpr size_t false_len = sizeof(false_literal) - 1; + if (capacity_check(false_len)) { + std::memcpy(buffer.get() + position, false_literal, false_len); + position += false_len; + } + } + } + else SIMDJSON_IF_CONSTEXPR(std::is_unsigned::value) { + constexpr size_t max_number_size = 20; + if (capacity_check(max_number_size)) { + using unsigned_type = typename std::make_unsigned::type; + unsigned_type pv = static_cast(v); + size_t dc = internal::digit_count(pv); + char *write_pointer = buffer.get() + position + dc - 1; + while (pv >= 100) { + memcpy(write_pointer - 1, &internal::decimal_table[(pv % 100) * 2], 2); + write_pointer -= 2; + pv /= 100; + } + if (pv >= 10) { + *write_pointer-- = char('0' + (pv % 10)); + pv /= 10; + } + *write_pointer = char('0' + pv); + position += dc; + } + } + else SIMDJSON_IF_CONSTEXPR(std::is_integral::value) { + constexpr size_t max_number_size = 20; + if (capacity_check(max_number_size)) { + using unsigned_type = typename std::make_unsigned::type; + bool negative = v < 0; + unsigned_type pv = static_cast(v); + if (negative) { + pv = 0 - pv; // the 0 is for Microsoft + } + size_t dc = internal::digit_count(pv); + if (negative) { + buffer.get()[position++] = '-'; + } + char *write_pointer = buffer.get() + position + dc - 1; + while (pv >= 100) { + memcpy(write_pointer - 1, &internal::decimal_table[(pv % 100) * 2], 2); + write_pointer -= 2; + pv /= 100; + } + if (pv >= 10) { + *write_pointer-- = char('0' + (pv % 10)); + pv /= 10; + } + *write_pointer = char('0' + pv); + position += dc; + } + } + else SIMDJSON_IF_CONSTEXPR(std::is_floating_point::value) { + constexpr size_t max_number_size = 24; + if (capacity_check(max_number_size)) { + // We could specialize for float. + char *end = simdjson::internal::to_chars(buffer.get() + position, nullptr, + double(v)); + position = end - buffer.get(); + } + } +} + +simdjson_inline void +string_builder::escape_and_append(std::string_view input) noexcept { + // escaping might turn a control character into \x00xx so 6 characters. + if (capacity_check(6 * input.size())) { + position += write_string_escaped(input, buffer.get() + position); + } +} + +simdjson_inline void +string_builder::escape_and_append_with_quotes(std::string_view input) noexcept { + // escaping might turn a control character into \x00xx so 6 characters. + if (capacity_check(2 + 6 * input.size())) { + buffer.get()[position++] = '"'; + position += write_string_escaped(input, buffer.get() + position); + buffer.get()[position++] = '"'; + } +} + +simdjson_inline void +string_builder::escape_and_append_with_quotes(char input) noexcept { + // escaping might turn a control character into \x00xx so 6 characters. + if (capacity_check(2 + 6 * 1)) { + buffer.get()[position++] = '"'; + std::string_view cinput(&input, 1); + position += write_string_escaped(cinput, buffer.get() + position); + buffer.get()[position++] = '"'; + } +} + +simdjson_inline void +string_builder::escape_and_append_with_quotes(const char *input) noexcept { + std::string_view cinput(input); + escape_and_append_with_quotes(cinput); +} +#if SIMDJSON_SUPPORTS_CONCEPTS +template +simdjson_inline void string_builder::escape_and_append_with_quotes() noexcept { + escape_and_append_with_quotes(constevalutil::string_constant::value); +} +#endif + +simdjson_inline void string_builder::append_raw(const char *c) noexcept { + size_t len = std::strlen(c); + append_raw(c, len); +} + +simdjson_inline void +string_builder::append_raw(std::string_view input) noexcept { + if (capacity_check(input.size())) { + std::memcpy(buffer.get() + position, input.data(), input.size()); + position += input.size(); + } +} + +simdjson_inline void string_builder::append_raw(const char *str, + size_t len) noexcept { + if (capacity_check(len)) { + std::memcpy(buffer.get() + position, str, len); + position += len; + } +} +#if SIMDJSON_SUPPORTS_CONCEPTS +// Support for optional types (std::optional, etc.) +template + requires(!require_custom_serialization) +simdjson_inline void string_builder::append(const T &opt) { + if (opt) { + append(*opt); + } else { + append_null(); + } +} + +template + requires(require_custom_serialization) +simdjson_inline void string_builder::append(const T &val) { + serialize(*this, val); +} + +template + requires(std::is_convertible::value || + std::is_same::value) +simdjson_inline void string_builder::append(const T &value) { + escape_and_append_with_quotes(value); +} +#endif + +#if SIMDJSON_SUPPORTS_RANGES && SIMDJSON_SUPPORTS_CONCEPTS +// Support for range-based appending (std::ranges::view, etc.) +template + requires(!std::is_convertible::value) +simdjson_inline void string_builder::append(const R &range) noexcept { + auto it = std::ranges::begin(range); + auto end = std::ranges::end(range); + if constexpr (concepts::is_pair) { + start_object(); + + if (it == end) { + end_object(); + return; // Handle empty range + } + // Append first item without leading comma + append_key_value(it->first, it->second); + ++it; + + // Append remaining items with preceding commas + for (; it != end; ++it) { + append_comma(); + append_key_value(it->first, it->second); + } + end_object(); + } else { + start_array(); + if (it == end) { + end_array(); + return; // Handle empty range + } + + // Append first item without leading comma + append(*it); + ++it; + + // Append remaining items with preceding commas + for (; it != end; ++it) { + append_comma(); + append(*it); + } + end_array(); + } +} + +#endif + +#if SIMDJSON_EXCEPTIONS +simdjson_inline string_builder::operator std::string() const noexcept(false) { + return std::string(operator std::string_view()); +} + +simdjson_inline string_builder::operator std::string_view() const + noexcept(false) simdjson_lifetime_bound { + return view(); +} +#endif + +simdjson_inline simdjson_result +string_builder::view() const noexcept { + if (!is_valid) { + return simdjson::OUT_OF_CAPACITY; + } + return std::string_view(buffer.get(), position); +} + +simdjson_inline simdjson_result string_builder::c_str() noexcept { + if (capacity_check(1)) { + buffer.get()[position] = '\0'; + return buffer.get(); + } + return simdjson::OUT_OF_CAPACITY; +} + +simdjson_inline bool string_builder::validate_unicode() const noexcept { + return simdjson::validate_utf8(buffer.get(), position); +} + +simdjson_inline void string_builder::start_object() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = '{'; + } +} + +simdjson_inline void string_builder::end_object() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = '}'; + } +} + +simdjson_inline void string_builder::start_array() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = '['; + } +} + +simdjson_inline void string_builder::end_array() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = ']'; + } +} + +simdjson_inline void string_builder::append_comma() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = ','; + } +} + +simdjson_inline void string_builder::append_colon() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = ':'; + } +} + +template +simdjson_inline void +string_builder::append_key_value(key_type key, value_type value) noexcept { + static_assert(std::is_same::value || + std::is_convertible::value, + "Unsupported key type"); + escape_and_append_with_quotes(key); + append_colon(); + SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + append_null(); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR( + std::is_convertible::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else { + append(value); + } +} + +#if SIMDJSON_SUPPORTS_CONCEPTS +template +simdjson_inline void +string_builder::append_key_value(value_type value) noexcept { + escape_and_append_with_quotes(); + append_colon(); + SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + append_null(); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR( + std::is_convertible::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else { + append(value); + } +} +#endif + +} // namespace builder +} // namespace arm64 +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_STRING_BUILDER_INL_H +/* end file simdjson/generic/ondemand/json_string_builder-inl.h for arm64 */ +/* including simdjson/generic/ondemand/json_builder.h for arm64: #include "simdjson/generic/ondemand/json_builder.h" */ +/* begin file simdjson/generic/ondemand/json_builder.h for arm64 */ +/** + * This file is part of the builder API. It is temporarily in the ondemand directory + * but we will move it to a builder directory later. + */ +#ifndef SIMDJSON_GENERIC_BUILDER_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_string_builder.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/concepts.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ +#if SIMDJSON_STATIC_REFLECTION + +#include +#include +#include +#include +#include +#include +#include +#include +// #include // for std::define_static_string - header not available yet + +namespace simdjson { +namespace arm64 { +namespace builder { + +template + requires(concepts::container_but_not_string && !require_custom_serialization) +constexpr void atom(string_builder &b, const T &t) { + auto it = t.begin(); + auto end = t.end(); + if (it == end) { + b.append_raw("[]"); + return; + } + b.append('['); + atom(b, *it); + ++it; + for (; it != end; ++it) { + b.append(','); + atom(b, *it); + } + b.append(']'); +} + +template + requires(std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v) +constexpr void atom(string_builder &b, const T &t) { + b.escape_and_append_with_quotes(t); +} + +template + requires(!require_custom_serialization) +constexpr void atom(string_builder &b, const T &m) { + if (m.empty()) { + b.append_raw("{}"); + return; + } + b.append('{'); + bool first = true; + for (const auto& [key, value] : m) { + if (!first) { + b.append(','); + } + first = false; + // Keys must be convertible to string_view per the concept + b.escape_and_append_with_quotes(key); + b.append(':'); + atom(b, value); + } + b.append('}'); +} + + +template::value && !std::is_same_v>::type> +constexpr void atom(string_builder &b, const number_type t) { + b.append(t); +} + +template + requires(std::is_class_v && !concepts::container_but_not_string && + !concepts::string_view_keyed_map && + !concepts::optional_type && + !concepts::smart_pointer && + !concepts::appendable_containers && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && !require_custom_serialization) +constexpr void atom(string_builder &b, const T &t) { + int i = 0; + b.append('{'); + template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) { + if (i != 0) + b.append(','); + constexpr auto key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(dm))); + b.append_raw(key); + b.append(':'); + atom(b, t.[:dm:]); + i++; + }; + b.append('}'); +} + +// Support for optional types (std::optional, etc.) +template + requires(!require_custom_serialization) +constexpr void atom(string_builder &b, const T &opt) { + if (opt) { + atom(b, opt.value()); + } else { + b.append_raw("null"); + } +} + +// Support for smart pointers (std::unique_ptr, std::shared_ptr, etc.) +template + requires(!require_custom_serialization) +constexpr void atom(string_builder &b, const T &ptr) { + if (ptr) { + atom(b, *ptr); + } else { + b.append_raw("null"); + } +} + +// Support for enums - serialize as string representation using expand approach from P2996R12 +template + requires(std::is_enum_v && !require_custom_serialization) +void atom(string_builder &b, const T &e) { +#if SIMDJSON_STATIC_REFLECTION + constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T)); + template for (constexpr auto enum_val : enumerators) { + constexpr auto enum_str = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(enum_val))); + if (e == [:enum_val:]) { + b.append_raw(enum_str); + return; + } + }; + // Fallback to integer if enum value not found + atom(b, static_cast>(e)); +#else + // Fallback: serialize as integer if reflection not available + atom(b, static_cast>(e)); +#endif +} + +// Support for appendable containers that don't have operator[] (sets, etc.) +template + requires(!concepts::container_but_not_string && !concepts::string_view_keyed_map && + !concepts::optional_type && !concepts::smart_pointer && + !std::is_same_v && + !std::is_same_v && !std::is_same_v && !require_custom_serialization) +constexpr void atom(string_builder &b, const T &container) { + if (container.empty()) { + b.append_raw("[]"); + return; + } + b.append('['); + bool first = true; + for (const auto& item : container) { + if (!first) { + b.append(','); + } + first = false; + atom(b, item); + } + b.append(']'); +} + +// append functions that delegate to atom functions for primitive types +template + requires(std::is_arithmetic_v && !std::is_same_v) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!concepts::container_but_not_string && !concepts::string_view_keyed_map && + !concepts::optional_type && !concepts::smart_pointer && + !std::is_same_v && + !std::is_same_v && !std::is_same_v && !require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +// works for struct +template + requires(std::is_class_v && !concepts::container_but_not_string && + !concepts::string_view_keyed_map && + !concepts::optional_type && + !concepts::smart_pointer && + !concepts::appendable_containers && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && !require_custom_serialization) +void append(string_builder &b, const Z &z) { + int i = 0; + b.append('{'); + template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^Z, std::meta::access_context::unchecked()))) { + if (i != 0) + b.append(','); + constexpr auto key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(dm))); + b.append_raw(key); + b.append(':'); + atom(b, z.[:dm:]); + i++; + }; + b.append('}'); +} + +// works for container that have begin() and end() iterators +template + requires(concepts::container_but_not_string && !require_custom_serialization) +void append(string_builder &b, const Z &z) { + auto it = z.begin(); + auto end = z.end(); + if (it == end) { + b.append_raw("[]"); + return; + } + b.append('['); + atom(b, *it); + ++it; + for (; it != end; ++it) { + b.append(','); + atom(b, *it); + } + b.append(']'); +} + +template + requires (require_custom_serialization) +void append(string_builder &b, const Z &z) { + b.append(z); +} + + +template +simdjson_warn_unused simdjson_result to_json_string(const Z &z, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) { + string_builder b(initial_capacity); + append(b, z); + std::string_view s; + if(auto e = b.view().get(s); e) { return e; } + return std::string(s); +} + +template +simdjson_warn_unused simdjson_error to_json(const Z &z, std::string &s, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) { + string_builder b(initial_capacity); + append(b, z); + std::string_view view; + if(auto e = b.view().get(view); e) { return e; } + s.assign(view); + return SUCCESS; +} + +template +string_builder& operator<<(string_builder& b, const Z& z) { + append(b, z); + return b; +} + +// extract_from: Serialize only specific fields from a struct to JSON +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +void extract_from(string_builder &b, const T &obj) { + // Helper to check if a field name matches any of the requested fields + auto should_extract = [](std::string_view field_name) constexpr -> bool { + return ((FieldNames.view() == field_name) || ...); + }; + + b.append('{'); + bool first = true; + + // Iterate through all members of T using reflection + template for (constexpr auto mem : std::define_static_array( + std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) { + + if constexpr (std::meta::is_public(mem)) { + constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem)); + + // Only serialize this field if it's in our list of requested fields + if constexpr (should_extract(key)) { + if (!first) { + b.append(','); + } + first = false; + + // Serialize the key + constexpr auto quoted_key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(mem))); + b.append_raw(quoted_key); + b.append(':'); + + // Serialize the value + atom(b, obj.[:mem:]); + } + } + }; + + b.append('}'); +} + +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_result extract_from(const T &obj, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) { + string_builder b(initial_capacity); + extract_from(b, obj); + std::string_view s; + if(auto e = b.view().get(s); e) { return e; } + return std::string(s); +} + +} // namespace builder +} // namespace arm64 +// Alias the function template to 'to' in the global namespace +template +simdjson_warn_unused simdjson_result to_json(const Z &z, size_t initial_capacity = arm64::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { + arm64::builder::string_builder b(initial_capacity); + arm64::builder::append(b, z); + std::string_view s; + if(auto e = b.view().get(s); e) { return e; } + return std::string(s); +} +template +simdjson_warn_unused simdjson_error to_json(const Z &z, std::string &s, size_t initial_capacity = arm64::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { + arm64::builder::string_builder b(initial_capacity); + arm64::builder::append(b, z); + std::string_view view; + if(auto e = b.view().get(view); e) { return e; } + s.assign(view); + return SUCCESS; +} +// Global namespace function for extract_from +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_result extract_from(const T &obj, size_t initial_capacity = arm64::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { + arm64::builder::string_builder b(initial_capacity); + arm64::builder::extract_from(b, obj); + std::string_view s; + if(auto e = b.view().get(s); e) { return e; } + return std::string(s); +} + +} // namespace simdjson + +#endif // SIMDJSON_STATIC_REFLECTION + +#endif +/* end file simdjson/generic/ondemand/json_builder.h for arm64 */ /* end file simdjson/generic/ondemand/amalgamated.h for arm64 */ /* including simdjson/arm64/end.h: #include "simdjson/arm64/end.h" */ @@ -42952,7 +46538,7 @@ namespace { struct backslash_and_quote { public: static constexpr uint32_t BYTES_PROCESSED = 1; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); + simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); simdjson_inline bool has_quote_first() { return c == '"'; } simdjson_inline bool has_backslash() { return c == '\\'; } @@ -42968,6 +46554,24 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin return { src[0] }; } + +struct escaping { + static constexpr uint32_t BYTES_PROCESSED = 1; + simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst); + + simdjson_inline bool has_escape() { return escape_bits; } + simdjson_inline int escape_index() { return 0; } + + bool escape_bits; +}; // struct escaping + + + +simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) { + dst[0] = src[0]; + return { (src[0] == '\\') || (src[0] == '"') || (src[0] < 32) }; +} + } // unnamed namespace } // namespace fallback } // namespace simdjson @@ -43123,7 +46727,7 @@ class value_iterator; /* end file simdjson/generic/ondemand/base.h for fallback */ /* including simdjson/generic/ondemand/deserialize.h for fallback: #include "simdjson/generic/ondemand/deserialize.h" */ /* begin file simdjson/generic/ondemand/deserialize.h for fallback */ -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS #ifndef SIMDJSON_ONDEMAND_DESERIALIZE_H /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ @@ -43132,55 +46736,8 @@ class value_iterator; /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ -#include namespace simdjson { -namespace tag_invoke_fn_ns { -void tag_invoke(); - -struct tag_invoke_fn { - template - requires requires(Tag tag, Args &&...args) { - tag_invoke(std::forward(tag), std::forward(args)...); - } - constexpr auto operator()(Tag tag, Args &&...args) const - noexcept(noexcept(tag_invoke(std::forward(tag), - std::forward(args)...))) - -> decltype(tag_invoke(std::forward(tag), - std::forward(args)...)) { - return tag_invoke(std::forward(tag), std::forward(args)...); - } -}; -} // namespace tag_invoke_fn_ns - -inline namespace tag_invoke_ns { -inline constexpr tag_invoke_fn_ns::tag_invoke_fn tag_invoke = {}; -} // namespace tag_invoke_ns - -template -concept tag_invocable = requires(Tag tag, Args... args) { - tag_invoke(std::forward(tag), std::forward(args)...); -}; - -template -concept nothrow_tag_invocable = - tag_invocable && requires(Tag tag, Args... args) { - { - tag_invoke(std::forward(tag), std::forward(args)...) - } noexcept; - }; - -template -using tag_invoke_result = - std::invoke_result; - -template -using tag_invoke_result_t = - std::invoke_result_t; - -template using tag_t = std::decay_t; - - struct deserialize_tag; /// These types are deserializable in a built-in way @@ -43202,7 +46759,7 @@ template concept custom_deserializable = tag_invocable; template -concept deserializable = custom_deserializable || is_builtin_deserializable_v; +concept deserializable = custom_deserializable || is_builtin_deserializable_v || concepts::optional_type; template concept nothrow_custom_deserializable = nothrow_tag_invocable; @@ -43213,28 +46770,44 @@ concept nothrow_deserializable = nothrow_custom_deserializable || is_bu /// Deserialize Tag inline constexpr struct deserialize_tag { + using array_type = fallback::ondemand::array; + using object_type = fallback::ondemand::object; using value_type = fallback::ondemand::value; using document_type = fallback::ondemand::document; using document_reference_type = fallback::ondemand::document_reference; + // Customization Point for array + template + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(array_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for object + template + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(object_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + // Customization Point for value template requires custom_deserializable - [[nodiscard]] constexpr /* error_code */ auto operator()(value_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + simdjson_warn_unused constexpr /* error_code */ auto operator()(value_type &object, T& output) const noexcept(nothrow_custom_deserializable) { return tag_invoke(*this, object, output); } // Customization Point for document template requires custom_deserializable - [[nodiscard]] constexpr /* error_code */ auto operator()(document_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + simdjson_warn_unused constexpr /* error_code */ auto operator()(document_type &object, T& output) const noexcept(nothrow_custom_deserializable) { return tag_invoke(*this, object, output); } // Customization Point for document reference template requires custom_deserializable - [[nodiscard]] constexpr /* error_code */ auto operator()(document_reference_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + simdjson_warn_unused constexpr /* error_code */ auto operator()(document_reference_type &object, T& output) const noexcept(nothrow_custom_deserializable) { return tag_invoke(*this, object, output); } @@ -43244,7 +46817,7 @@ inline constexpr struct deserialize_tag { } // namespace simdjson #endif // SIMDJSON_ONDEMAND_DESERIALIZE_H -#endif // SIMDJSON_SUPPORTS_DESERIALIZATION +#endif // SIMDJSON_SUPPORTS_CONCEPTS /* end file simdjson/generic/ondemand/deserialize.h for fallback */ /* including simdjson/generic/ondemand/value_iterator.h for fallback: #include "simdjson/generic/ondemand/value_iterator.h" */ @@ -43586,7 +47159,7 @@ class value_iterator { simdjson_warn_unused simdjson_inline simdjson_result get_root_number(bool check_trailing) noexcept; simdjson_warn_unused simdjson_inline simdjson_result is_root_null(bool check_trailing) noexcept; - simdjson_inline error_code error() const noexcept; + simdjson_warn_unused simdjson_inline error_code error() const noexcept; simdjson_inline uint8_t *&string_buf_loc() noexcept; simdjson_inline const json_iterator &json_iter() const noexcept; simdjson_inline json_iterator &json_iter() noexcept; @@ -43670,8 +47243,8 @@ class value_iterator { simdjson_inline const uint8_t *peek_non_root_scalar(const char *type) noexcept; - simdjson_inline error_code start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept; - simdjson_inline error_code end_container() noexcept; + simdjson_warn_unused simdjson_inline error_code start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept; + simdjson_warn_unused simdjson_inline error_code end_container() noexcept; /** * Advance to a place expecting a value (increasing depth). @@ -43681,8 +47254,8 @@ class value_iterator { */ simdjson_inline simdjson_result advance_to_value() noexcept; - simdjson_inline error_code incorrect_type_error(const char *message) const noexcept; - simdjson_inline error_code error_unless_more_tokens(uint32_t tokens=1) const noexcept; + simdjson_warn_unused simdjson_inline error_code incorrect_type_error(const char *message) const noexcept; + simdjson_warn_unused simdjson_inline error_code error_unless_more_tokens(uint32_t tokens=1) const noexcept; simdjson_inline bool is_at_start() const noexcept; /** @@ -43719,7 +47292,7 @@ class value_iterator { /** @copydoc error_code json_iterator::end_position() const noexcept; */ simdjson_inline token_position end_position() const noexcept; /** @copydoc error_code json_iterator::report_error(error_code error, const char *message) noexcept; */ - simdjson_inline error_code report_error(error_code error, const char *message) noexcept; + simdjson_warn_unused simdjson_inline error_code report_error(error_code error, const char *message) noexcept; friend class document; friend class object; @@ -43784,13 +47357,14 @@ class value { * * You may use get_double(), get_bool(), get_uint64(), get_int64(), * get_object(), get_array(), get_raw_json_string(), or get_string() instead. + * When SIMDJSON_SUPPORTS_CONCEPTS is set, custom types are also supported. * * @returns A value of the given type, parsed from the JSON. * @returns INCORRECT_TYPE If the JSON value is not the given type. */ template simdjson_inline simdjson_result get() -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept @@ -43807,22 +47381,38 @@ class value { * Get this value as the given type. * * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool + * If the macro SIMDJSON_SUPPORTS_CONCEPTS is set, then custom types are also supported. * * @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized. * @returns INCORRECT_TYPE If the JSON value is not an object. * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. */ template - simdjson_inline error_code get(T &out) -#if SIMDJSON_SUPPORTS_DESERIALIZATION + simdjson_warn_unused simdjson_inline error_code get(T &out) +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept #endif { - #if SIMDJSON_SUPPORTS_DESERIALIZATION + #if SIMDJSON_SUPPORTS_CONCEPTS if constexpr (custom_deserializable) { return deserialize(*this, out); + } else if constexpr (concepts::optional_type) { + using value_type = typename std::remove_cvref_t::value_type; + + // Check if the value is null + bool is_null_value; + SIMDJSON_TRY( is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); // Set to nullopt + return SUCCESS; + } + + if (!out) { + out.emplace(); + } + return get(out.value()); } else { static_assert(!sizeof(T), "The get method with type T is not implemented by the simdjson library. " "And you do not seem to have added support for it. Indeed, we have that " @@ -43832,7 +47422,7 @@ class value { static_cast(out); // to get rid of unused errors return UNINITIALIZED; } -#else // SIMDJSON_SUPPORTS_DESERIALIZATION +#else // SIMDJSON_SUPPORTS_CONCEPTS // Unless the simdjson library or the user provides an inline implementation, calling this method should // immediately fail. static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " @@ -43950,7 +47540,7 @@ class value { * @returns INCORRECT_TYPE if the JSON value is not a string. Otherwise, we return SUCCESS. */ template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; /** * Cast this JSON value to a "wobbly" string. @@ -44471,7 +48061,7 @@ struct simdjson_result : public fallback::implementat simdjson_inline simdjson_result get_double_in_string() noexcept; simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; simdjson_inline simdjson_result get_wobbly_string() noexcept; simdjson_inline simdjson_result get_raw_json_string() noexcept; simdjson_inline simdjson_result get_bool() noexcept; @@ -44552,7 +48142,22 @@ struct simdjson_result : public fallback::implementat simdjson_result operator[](int) noexcept = delete; /** - * Get the type of this JSON value. + * Get the type of this JSON value. It does not validate or consume the value. + * E.g., you must still call "is_null()" to check that a value is null even if + * "type()" returns json_type::null. + * + * Given a valid JSON document, the answer can be one of + * simdjson::ondemand::json_type::object, + * simdjson::ondemand::json_type::array, + * simdjson::ondemand::json_type::string, + * simdjson::ondemand::json_type::number, + * simdjson::ondemand::json_type::boolean, + * simdjson::ondemand::json_type::null. + * + * Starting with simdjson 4.0, this function will return simdjson::ondemand::json_type::unknown + * given a bad token. + * This allows you to identify a case such as {"key": NaN} and identify the NaN value. + * The simdjson::ondemand::json_type::unknown value should only happen with non-valid JSON. * * NOTE: If you're only expecting a value to be one type (a typical case), it's generally * better to just call .get_double, .get_string, etc. and check for INCORRECT_TYPE (or just @@ -45046,14 +48651,14 @@ class json_iterator { * @param error The error to report. Must not be SUCCESS, UNINITIALIZED, INCORRECT_TYPE, or NO_SUCH_FIELD. * @param message An error message to report with the error. */ - simdjson_inline error_code report_error(error_code error, const char *message) noexcept; + simdjson_warn_unused simdjson_inline error_code report_error(error_code error, const char *message) noexcept; /** * Log error, but don't stop iteration. * @param error The error to report. Must be INCORRECT_TYPE, or NO_SUCH_FIELD. * @param message An error message to report with the error. */ - simdjson_inline error_code optional_error(error_code error, const char *message) noexcept; + simdjson_warn_unused simdjson_inline error_code optional_error(error_code error, const char *message) noexcept; /** * Take an input in json containing max_len characters and attempt to copy it over to tmpbuf, a buffer with @@ -45073,7 +48678,7 @@ class json_iterator { simdjson_inline void reenter_child(token_position position, depth_t child_depth) noexcept; - simdjson_inline error_code consume_character(char c) noexcept; + simdjson_warn_unused simdjson_inline error_code consume_character(char c) noexcept; #if SIMDJSON_DEVELOPMENT_CHECKS simdjson_inline token_position start_position(depth_t depth) const noexcept; simdjson_inline void set_start_position(depth_t depth, token_position position) noexcept; @@ -45164,6 +48769,7 @@ namespace ondemand { * The type of a JSON value. */ enum class json_type { + unknown=0, // Start at 1 to catch uninitialized / default values more easily array=1, ///< A JSON array ( [ 1, 2, 3 ... ] ) object, ///< A JSON object ( { "a": 1, "b" 2, ... } ) @@ -45370,6 +48976,12 @@ class raw_json_string { */ simdjson_inline const char * raw() const noexcept; + /** + * Get the character at index i. This is unchecked. + * [0] when the string is of length 0 returns the final quote ("). + */ + simdjson_inline char operator[](size_t i) const noexcept; + /** * This compares the current instance to the std::string_view target: returns true if * they are byte-by-byte equal (no escaping is done) on target.size() characters, @@ -45509,10 +49121,10 @@ struct simdjson_result : public fallback::i simdjson_inline ~simdjson_result() noexcept = default; ///< @private simdjson_inline simdjson_result raw() const noexcept; + simdjson_inline char operator[](size_t) const noexcept; simdjson_inline simdjson_warn_unused simdjson_result unescape(fallback::ondemand::json_iterator &iter, bool allow_replacement) const noexcept; simdjson_inline simdjson_warn_unused simdjson_result unescape_wobbly(fallback::ondemand::json_iterator &iter) const noexcept; }; - } // namespace simdjson #endif // SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_H @@ -45528,6 +49140,7 @@ struct simdjson_result : public fallback::i /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ #include +#include namespace simdjson { namespace fallback { @@ -45646,7 +49259,9 @@ class parser { simdjson_warn_unused simdjson_result iterate(std::string_view json, size_t capacity) & noexcept; /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ simdjson_warn_unused simdjson_result iterate(const std::string &json) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + /** @overload simdjson_result iterate(padded_string_view json) & noexcept + The string instance might be have its capacity extended. Note that this can still + result in AddressSanitizer: container-overflow in some cases. */ simdjson_warn_unused simdjson_result iterate(std::string &json) & noexcept; /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ simdjson_warn_unused simdjson_result iterate(const simdjson_result &json) & noexcept; @@ -45734,6 +49349,11 @@ class parser { * Setting batch_size to excessively large or excessively small values may impact negatively the * performance. * + * ### Threads + * + * When compiled with SIMDJSON_THREADS_ENABLED, this method will use a single thread under the + * hood to do some lookahead. + * * ### REQUIRED: Buffer Padding * * The buffer must have at least SIMDJSON_PADDING extra allocated bytes. It does not matter what @@ -45741,10 +49361,10 @@ class parser { * using a sanitizer that verifies that no uninitialized byte is read, then you should initialize the * SIMDJSON_PADDING bytes to avoid runtime warnings. * - * ### Threads + * This is checked automatically with all iterate_many function calls, except for the two + * that take pointers (const char* or const uint8_t*). * - * When compiled with SIMDJSON_THREADS_ENABLED, this method will use a single thread under the - * hood to do some lookahead. + * ### Threads * * ### Parser Capacity * @@ -45770,14 +49390,16 @@ class parser { */ inline simdjson_result iterate_many(const uint8_t *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ + inline simdjson_result iterate_many(padded_string_view json, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ inline simdjson_result iterate_many(const char *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ inline simdjson_result iterate_many(const std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; - inline simdjson_result iterate_many(const std::string &&s, size_t batch_size, bool allow_comma_separated = false) = delete;// unsafe + /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) + the string might be automatically padded with up to SIMDJSON_PADDING whitespace characters */ + inline simdjson_result iterate_many(std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ inline simdjson_result iterate_many(const padded_string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; - inline simdjson_result iterate_many(const padded_string &&s, size_t batch_size, bool allow_comma_separated = false) = delete;// unsafe - /** @private We do not want to allow implicit conversion from C string to std::string. */ simdjson_result iterate_many(const char *buf, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept = delete; @@ -45879,13 +49501,39 @@ class parser { bool string_buffer_overflow(const uint8_t *string_buf_loc) const noexcept; #endif + /** + * Get a unique parser instance corresponding to the current thread. + * This instance can be safely used within the current thread, but it should + * not be passed to other threads. + * + * A parser should only be used for one document at a time. + * + * Our simdjson::from functions use this parser instance. + * + * You can free the related parser by calling release_parser(). + */ + static simdjson_inline simdjson_warn_unused ondemand::parser& get_parser(); + /** + * Release the parser instance initialized by get_parser() and all the + * associated resources (memory). Returns true if a parser instance + * was released. + */ + static simdjson_inline bool release_parser(); + private: + friend bool release_parser(); + friend ondemand::parser& get_parser(); + /** Get the thread-local parser instance, allocates it if needed */ + static simdjson_inline simdjson_warn_unused std::unique_ptr& get_parser_instance(); + /** Get the thread-local parser instance, it might be null */ + static simdjson_inline simdjson_warn_unused std::unique_ptr& get_threadlocal_parser_if_exists(); /** @private [for benchmarking access] The implementation to use */ std::unique_ptr implementation{}; size_t _capacity{0}; size_t _max_capacity; size_t _max_depth{DEFAULT_MAX_DEPTH}; std::unique_ptr string_buf{}; + #if SIMDJSON_DEVELOPMENT_CHECKS std::unique_ptr start_positions{}; #endif @@ -45913,6 +49561,315 @@ struct simdjson_result : public fallback::implementa #endif // SIMDJSON_GENERIC_ONDEMAND_PARSER_H /* end file simdjson/generic/ondemand/parser.h for fallback */ +// JSON builder - needed for extract_into functionality +/* including simdjson/generic/ondemand/json_string_builder.h for fallback: #include "simdjson/generic/ondemand/json_string_builder.h" */ +/* begin file simdjson/generic/ondemand/json_string_builder.h for fallback */ +/** + * This file is part of the builder API. It is temporarily in the ondemand directory + * but we will move it to a builder directory later. + */ +#ifndef SIMDJSON_GENERIC_STRING_BUILDER_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { + + +#if SIMDJSON_SUPPORTS_CONCEPTS + +namespace fallback { +namespace builder { + class string_builder; +}} + +template +struct has_custom_serialization : std::false_type {}; + +inline constexpr struct serialize_tag { + template + requires custom_deserializable + constexpr void operator()(fallback::builder::string_builder& b, T& obj) const{ + return tag_invoke(*this, b, obj); + } + + +} serialize{}; +template +struct has_custom_serialization(), std::declval())) +>> : std::true_type {}; + +template +constexpr bool require_custom_serialization = has_custom_serialization::value; +#else +struct has_custom_serialization : std::false_type {}; +#endif // SIMDJSON_SUPPORTS_CONCEPTS + +namespace fallback { +namespace builder { +/** + * A builder for JSON strings representing documents. This is a low-level + * builder that is not meant to be used directly by end-users. Though it + * supports atomic types (Booleans, strings), it does not support composed + * types (arrays and objects). + * + * Ultimately, this class can support kernel-specific optimizations. E.g., + * it may make use of SIMD instructions to escape strings faster. + */ +class string_builder { +public: + simdjson_inline string_builder(size_t initial_capacity = DEFAULT_INITIAL_CAPACITY); + + static constexpr size_t DEFAULT_INITIAL_CAPACITY = 1024; + + /** + * Append number (includes Booleans). Booleans are mapped to the strings + * false and true. Numbers are converted to strings abiding by the JSON standard. + * Floating-point numbers are converted to the shortest string that 'correctly' + * represents the number. + */ + template::value>::type> + simdjson_inline void append(number_type v) noexcept; + + /** + * Append character c. + */ + simdjson_inline void append(char c) noexcept; + + /** + * Append the string 'null'. + */ + simdjson_inline void append_null() noexcept; + + /** + * Clear the content. + */ + simdjson_inline void clear() noexcept; + + /** + * Append the std::string_view, after escaping it. + * There is no UTF-8 validation. + */ + simdjson_inline void escape_and_append(std::string_view input) noexcept; + + /** + * Append the std::string_view surrounded by double quotes, after escaping it. + * There is no UTF-8 validation. + */ + simdjson_inline void escape_and_append_with_quotes(std::string_view input) noexcept; +#if SIMDJSON_SUPPORTS_CONCEPTS + template + simdjson_inline void escape_and_append_with_quotes() noexcept; +#endif + /** + * Append the character surrounded by double quotes, after escaping it. + * There is no UTF-8 validation. + */ + simdjson_inline void escape_and_append_with_quotes(char input) noexcept; + + /** + * Append the character surrounded by double quotes, after escaping it. + * There is no UTF-8 validation. + */ + simdjson_inline void escape_and_append_with_quotes(const char* input) noexcept; + + /** + * Append the C string directly, without escaping. + * There is no UTF-8 validation. + */ + simdjson_inline void append_raw(const char *c) noexcept; + + /** + * Append "{" to the buffer. + */ + simdjson_inline void start_object() noexcept; + + /** + * Append "}" to the buffer. + */ + simdjson_inline void end_object() noexcept; + + /** + * Append "[" to the buffer. + */ + simdjson_inline void start_array() noexcept; + + /** + * Append "]" to the buffer. + */ + simdjson_inline void end_array() noexcept; + + /** + * Append "," to the buffer. + */ + simdjson_inline void append_comma() noexcept; + + /** + * Append ":" to the buffer. + */ + simdjson_inline void append_colon() noexcept; + + /** + * Append a key-value pair to the buffer. + * The key is escaped and surrounded by double quotes. + * The value is escaped if it is a string. + */ + template + simdjson_inline void append_key_value(key_type key, value_type value) noexcept; +#if SIMDJSON_SUPPORTS_CONCEPTS + template + simdjson_inline void append_key_value(value_type value) noexcept; + + // Support for optional types (std::optional, etc.) + template + requires(!require_custom_serialization) + simdjson_inline void append(const T &opt); + + template + requires(require_custom_serialization) + simdjson_inline void append(const T &val); + + // Support for string-like types + template + requires(std::is_convertible::value || + std::is_same::value ) + simdjson_inline void append(const T &value); +#endif +#if SIMDJSON_SUPPORTS_RANGES && SIMDJSON_SUPPORTS_CONCEPTS + // Support for range-based appending (std::ranges::view, etc.) + template +requires (!std::is_convertible::value) + simdjson_inline void append(const R &range) noexcept; +#endif + /** + * Append the std::string_view directly, without escaping. + * There is no UTF-8 validation. + */ + simdjson_inline void append_raw(std::string_view input) noexcept; + + /** + * Append len characters from str. + * There is no UTF-8 validation. + */ + simdjson_inline void append_raw(const char *str, size_t len) noexcept; +#if SIMDJSON_EXCEPTIONS + /** + * Creates an std::string from the written JSON buffer. + * Throws if memory allocation failed + * + * The result may not be valid UTF-8 if some of your content was not valid UTF-8. + * Use validate_unicode() to check the content if needed. + */ + simdjson_inline operator std::string() const noexcept(false); + + /** + * Creates an std::string_view from the written JSON buffer. + * Throws if memory allocation failed. + * + * The result may not be valid UTF-8 if some of your content was not valid UTF-8. + * Use validate_unicode() to check the content if needed. + */ + simdjson_inline operator std::string_view() const noexcept(false) simdjson_lifetime_bound; +#endif + + /** + * Returns a view on the written JSON buffer. Returns an error + * if memory allocation failed. + * + * The result may not be valid UTF-8 if some of your content was not valid UTF-8. + * Use validate_unicode() to check the content. + */ + simdjson_inline simdjson_result view() const noexcept; + + /** + * Appends the null character to the buffer and returns + * a pointer to the beginning of the written JSON buffer. + * Returns an error if memory allocation failed. + * The result is null-terminated. + * + * The result may not be valid UTF-8 if some of your content was not valid UTF-8. + * Use validate_unicode() to check the content. + */ + simdjson_inline simdjson_result c_str() noexcept; + + /** + * Return true if the content is valid UTF-8. + */ + simdjson_inline bool validate_unicode() const noexcept; + + /** + * Returns the current size of the written JSON buffer. + * If an error occurred, returns 0. + */ + simdjson_inline size_t size() const noexcept; + +private: + /** + * Returns true if we can write at least upcoming_bytes bytes. + * The underlying buffer is reallocated if needed. It is designed + * to be called before writing to the buffer. It should be fast. + */ + simdjson_inline bool capacity_check(size_t upcoming_bytes); + + /** + * Grow the buffer to at least desired_capacity bytes. + * If the allocation fails, is_valid is set to false. We expect + * that this function would not be repeatedly called. + */ + simdjson_inline void grow_buffer(size_t desired_capacity); + + /** + * We use this helper function to make sure that is_valid is kept consistent. + */ + simdjson_inline void set_valid(bool valid) noexcept; + + std::unique_ptr buffer{}; + size_t position{0}; + size_t capacity{0}; + bool is_valid{true}; +}; + + + +} +} + + +#if !SIMDJSON_STATIC_REFLECTION +// fallback implementation until we have static reflection +template +simdjson_warn_unused simdjson_result to_json(const Z &z, size_t initial_capacity = simdjson::fallback::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { + simdjson::fallback::builder::string_builder b(initial_capacity); + b.append(z); + std::string_view s; + auto e = b.view().get(s); + if(e) { return e; } + return std::string(s); +} +template +simdjson_warn_unused simdjson_error to_json(const Z &z, std::string &s, size_t initial_capacity = simdjson::fallback::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { + simdjson::fallback::builder::string_builder b(initial_capacity); + b.append(z); + std::string_view sv; + auto e = b.view().get(sv); + if(e) { return e; } + s.assign(sv.data(), sv.size()); + return simdjson::SUCCESS; +} +#endif + +#if SIMDJSON_SUPPORTS_CONCEPTS +#endif // SIMDJSON_SUPPORTS_CONCEPTS + +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_STRING_BUILDER_H +/* end file simdjson/generic/ondemand/json_string_builder.h for fallback */ + // All other declarations /* including simdjson/generic/ondemand/array.h for fallback: #include "simdjson/generic/ondemand/array.h" */ /* begin file simdjson/generic/ondemand/array.h for fallback */ @@ -46049,11 +50006,42 @@ class array { * - INDEX_OUT_OF_BOUNDS if the array index is larger than an array length */ simdjson_inline simdjson_result at(size_t index) noexcept; + +#if SIMDJSON_SUPPORTS_CONCEPTS + /** + * Get this array as the given type. + * + * @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized. + * @returns INCORRECT_TYPE If the JSON array is not of the given type. + * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. + */ + template + simdjson_warn_unused simdjson_inline error_code get(T &out) + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) { + static_assert(custom_deserializable); + return deserialize(*this, out); + } + /** + * Get this array as the given type. + * + * @returns A value of the given type, parsed from the JSON. + * @returns INCORRECT_TYPE If the JSON value is not the given type. + */ + template + simdjson_inline simdjson_result get() + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) + { + static_assert(std::is_default_constructible::value, "The specified type is not default constructible."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } +#endif // SIMDJSON_SUPPORTS_CONCEPTS protected: /** * Go to the end of the array, no matter where you are right now. */ - simdjson_inline error_code consume() noexcept; + simdjson_warn_unused simdjson_inline error_code consume() noexcept; /** * Begin array iteration. @@ -46127,7 +50115,28 @@ struct simdjson_result : public fallback::implementat simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; simdjson_inline simdjson_result raw_json() noexcept; +#if SIMDJSON_SUPPORTS_CONCEPTS + // TODO: move this code into object-inl.h + template + simdjson_inline simdjson_result get() noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + return first; + } + return first.get(); + } + template + simdjson_warn_unused simdjson_inline error_code get(T& out) noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + out = first; + } else { + SIMDJSON_TRY( first.get(out) ); + } + return SUCCESS; + } +#endif // SIMDJSON_SUPPORTS_CONCEPTS }; } // namespace simdjson @@ -46172,7 +50181,8 @@ class array_iterator { * * Part of the std::iterator interface. */ - simdjson_inline simdjson_result operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. + simdjson_inline simdjson_result + operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. /** * Check if we are at the end of the JSON. * @@ -46196,6 +50206,11 @@ class array_iterator { */ simdjson_inline array_iterator &operator++() noexcept; + /** + * Check if the array is at the end. + */ + simdjson_warn_unused simdjson_inline bool at_end() const noexcept; + private: value_iterator iter{}; @@ -46214,7 +50229,6 @@ namespace simdjson { template<> struct simdjson_result : public fallback::implementation_simdjson_result_base { -public: simdjson_inline simdjson_result(fallback::ondemand::array_iterator &&value) noexcept; ///< @private simdjson_inline simdjson_result(error_code error) noexcept; ///< @private simdjson_inline simdjson_result() noexcept = default; @@ -46227,6 +50241,8 @@ struct simdjson_result : public fallback::im simdjson_inline bool operator==(const simdjson_result &) const noexcept; simdjson_inline bool operator!=(const simdjson_result &) const noexcept; simdjson_inline simdjson_result &operator++() noexcept; + + simdjson_warn_unused simdjson_inline bool at_end() const noexcept; }; } // namespace simdjson @@ -46354,7 +50370,7 @@ class document { * @returns INCORRECT_TYPE if the JSON value is not a string. Otherwise, we return SUCCESS. */ template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; /** * Cast this JSON value to a string. * @@ -46420,7 +50436,7 @@ class document { */ template simdjson_inline simdjson_result get() & -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept @@ -46443,7 +50459,7 @@ class document { */ template simdjson_inline simdjson_result get() && -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept @@ -46461,18 +50477,18 @@ class document { * Be mindful that the document instance must remain in scope while you are accessing object, array and value instances. * * @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized. - * @returns INCORRECT_TYPE If the JSON value is not an object. + * @returns INCORRECT_TYPE If the JSON value is of the given type. * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. */ template - simdjson_inline error_code get(T &out) & -#if SIMDJSON_SUPPORTS_DESERIALIZATION + simdjson_warn_unused simdjson_inline error_code get(T &out) & +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept #endif { -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS if constexpr (custom_deserializable) { return deserialize(*this, out); } else { @@ -46484,7 +50500,7 @@ class document { static_cast(out); // to get rid of unused errors return UNINITIALIZED; } -#else // SIMDJSON_SUPPORTS_DESERIALIZATION +#else // SIMDJSON_SUPPORTS_CONCEPTS // Unless the simdjson library or the user provides an inline implementation, calling this method should // immediately fail. static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " @@ -46494,7 +50510,7 @@ class document { " You may also add support for custom types, see our documentation."); static_cast(out); // to get rid of unused errors return UNINITIALIZED; -#endif // SIMDJSON_SUPPORTS_DESERIALIZATION +#endif // SIMDJSON_SUPPORTS_CONCEPTS } /** @overload template error_code get(T &out) & noexcept */ @@ -46559,7 +50575,7 @@ class document { * time it parses a document or when it is destroyed. * @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string. */ - simdjson_inline operator std::string_view() noexcept(false); + simdjson_inline operator std::string_view() noexcept(false) simdjson_lifetime_bound; /** * Cast this JSON value to a raw_json_string. * @@ -46568,7 +50584,7 @@ class document { * @returns A pointer to the raw JSON for the given string. * @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string. */ - simdjson_inline operator raw_json_string() noexcept(false); + simdjson_inline operator raw_json_string() noexcept(false) simdjson_lifetime_bound; /** * Cast this JSON value to a bool. * @@ -46718,11 +50734,27 @@ class document { * E.g., you must still call "is_null()" to check that a value is null even if * "type()" returns json_type::null. * + * The answer can be one of + * simdjson::ondemand::json_type::object, + * simdjson::ondemand::json_type::array, + * simdjson::ondemand::json_type::string, + * simdjson::ondemand::json_type::number, + * simdjson::ondemand::json_type::boolean, + * simdjson::ondemand::json_type::null. + * + * Starting with simdjson 4.0, this function will return simdjson::ondemand::json_type::unknown + * given a bad token. + * This allows you to identify a case such as {"key": NaN} and identify the NaN value. + * The simdjson::ondemand::json_type::unknown value should only happen with non-valid JSON. + * * NOTE: If you're only expecting a value to be one type (a typical case), it's generally * better to just call .get_double, .get_string, etc. and check for INCORRECT_TYPE (or just * let it throw an exception). * - * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". + * Prior to simdjson 4.0, this function would return an error given a bad token. + * Starting with simdjson 4.0, it will return simdjson::ondemand::json_type::unknown. + * This allows you to identify a case such as {"key": NaN} and identify the NaN value. + * The simdjson::ondemand::json_type::unknown value should only happen with non-valid JSON. */ simdjson_inline simdjson_result type() noexcept; @@ -46946,11 +50978,41 @@ class document { * the JSON document. */ simdjson_inline simdjson_result raw_json() noexcept; + +#if SIMDJSON_STATIC_REFLECTION + /** + * Extract only specific fields from the JSON object into a struct. + * + * This allows selective deserialization of only the fields you need, + * potentially improving performance by skipping unwanted fields. + * + * Example: + * ```c++ + * struct Car { + * std::string make; + * std::string model; + * int year; + * double price; + * }; + * + * Car car; + * doc.extract_into<"make", "model">(car); + * // Only 'make' and 'model' fields are extracted from JSON + * ``` + * + * @tparam FieldNames Compile-time string literals specifying which fields to extract + * @param out The output struct to populate with selected fields + * @returns SUCCESS on success, or an error code if a required field is missing or has wrong type + */ + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept; +#endif // SIMDJSON_STATIC_REFLECTION protected: /** * Consumes the document. */ - simdjson_inline error_code consume() noexcept; + simdjson_warn_unused simdjson_inline error_code consume() noexcept; simdjson_inline document(ondemand::json_iterator &&iter) noexcept; simdjson_inline const uint8_t *text(uint32_t idx) const noexcept; @@ -47003,7 +51065,7 @@ class document_reference { simdjson_inline simdjson_result get_double_in_string() noexcept; simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; simdjson_inline simdjson_result get_wobbly_string() noexcept; simdjson_inline simdjson_result get_raw_json_string() noexcept; simdjson_inline simdjson_result get_bool() noexcept; @@ -47012,7 +51074,7 @@ class document_reference { simdjson_inline simdjson_result is_null() noexcept; template simdjson_inline simdjson_result get() & -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept @@ -47025,7 +51087,7 @@ class document_reference { } template simdjson_inline simdjson_result get() && -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept @@ -47047,14 +51109,14 @@ class document_reference { * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. */ template - simdjson_inline error_code get(T &out) & -#if SIMDJSON_SUPPORTS_DESERIALIZATION + simdjson_warn_unused simdjson_inline error_code get(T &out) & +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept #endif { -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS if constexpr (custom_deserializable) { return deserialize(*this, out); } else { @@ -47066,7 +51128,7 @@ class document_reference { static_cast(out); // to get rid of unused errors return UNINITIALIZED; } -#else // SIMDJSON_SUPPORTS_DESERIALIZATION +#else // SIMDJSON_SUPPORTS_CONCEPTS // Unless the simdjson library or the user provides an inline implementation, calling this method should // immediately fail. static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " @@ -47076,12 +51138,17 @@ class document_reference { " You may also add support for custom types, see our documentation."); static_cast(out); // to get rid of unused errors return UNINITIALIZED; -#endif // SIMDJSON_SUPPORTS_DESERIALIZATION +#endif // SIMDJSON_SUPPORTS_CONCEPTS } /** @overload template error_code get(T &out) & noexcept */ template simdjson_inline error_code get(T &out) && noexcept; simdjson_inline simdjson_result raw_json() noexcept; +#if SIMDJSON_STATIC_REFLECTION + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept; +#endif // SIMDJSON_STATIC_REFLECTION simdjson_inline operator document&() const noexcept; #if SIMDJSON_EXCEPTIONS template @@ -47150,7 +51217,7 @@ struct simdjson_result : public fallback::implemen simdjson_inline simdjson_result get_double_in_string() noexcept; simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; simdjson_inline simdjson_result get_wobbly_string() noexcept; simdjson_inline simdjson_result get_raw_json_string() noexcept; simdjson_inline simdjson_result get_bool() noexcept; @@ -47163,6 +51230,9 @@ struct simdjson_result : public fallback::implemen template simdjson_inline error_code get(T &out) & noexcept; template simdjson_inline error_code get(T &out) && noexcept; #if SIMDJSON_EXCEPTIONS + + using fallback::implementation_simdjson_result_base::operator*; + using fallback::implementation_simdjson_result_base::operator->; template ::value == false>::type> explicit simdjson_inline operator T() noexcept(false); simdjson_inline operator fallback::ondemand::array() & noexcept(false); @@ -47202,6 +51272,11 @@ struct simdjson_result : public fallback::implemen simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; +#if SIMDJSON_STATIC_REFLECTION + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept; +#endif // SIMDJSON_STATIC_REFLECTION }; @@ -47228,7 +51303,7 @@ struct simdjson_result : public fallback simdjson_inline simdjson_result get_double_in_string() noexcept; simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; simdjson_inline simdjson_result get_wobbly_string() noexcept; simdjson_inline simdjson_result get_raw_json_string() noexcept; simdjson_inline simdjson_result get_bool() noexcept; @@ -47279,6 +51354,11 @@ struct simdjson_result : public fallback simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; +#if SIMDJSON_STATIC_REFLECTION + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept; +#endif // SIMDJSON_STATIC_REFLECTION }; @@ -47421,6 +51501,7 @@ class document_stream { * Default constructor. */ simdjson_inline iterator() noexcept; + simdjson_inline iterator(const iterator &other) noexcept = default; /** * Get the current document (or error). */ @@ -47434,6 +51515,7 @@ class document_stream { * @param other the end iterator to compare to. */ simdjson_inline bool operator!=(const iterator &other) const noexcept; + simdjson_inline bool operator==(const iterator &other) const noexcept; /** * @private * @@ -47477,6 +51559,11 @@ class document_stream { */ inline error_code error() const noexcept; + /** + * Returns whether the iterator is at the end. + */ + inline bool at_end() const noexcept; + private: simdjson_inline iterator(document_stream *s, bool finished) noexcept; /** The document_stream we're iterating through. */ @@ -47488,6 +51575,7 @@ class document_stream { friend class document_stream; friend class json_iterator; }; + using iterator = document_stream::iterator; /** * Start iterating the documents in the stream. @@ -47751,6 +51839,9 @@ struct simdjson_result : public fallback::implementat /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */ +/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION && SIMDJSON_SUPPORTS_CONCEPTS */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_string_builder.h" // for constevalutil::fixed_string */ +/* amalgamation skipped (editor-only): #endif */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { @@ -47948,11 +52039,71 @@ class object { */ simdjson_inline simdjson_result raw_json() noexcept; +#if SIMDJSON_SUPPORTS_CONCEPTS + /** + * Get this object as the given type. + * + * @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized. + * @returns INCORRECT_TYPE If the JSON object is not of the given type. + * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. + */ + template + simdjson_warn_unused simdjson_inline error_code get(T &out) + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) { + static_assert(custom_deserializable); + return deserialize(*this, out); + } + /** + * Get this array as the given type. + * + * @returns A value of the given type, parsed from the JSON. + * @returns INCORRECT_TYPE If the JSON value is not the given type. + */ + template + simdjson_inline simdjson_result get() + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) + { + static_assert(std::is_default_constructible::value, "The specified type is not default constructible."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } + +#if SIMDJSON_STATIC_REFLECTION + /** + * Extract only specific fields from the JSON object into a struct. + * + * This allows selective deserialization of only the fields you need, + * potentially improving performance by skipping unwanted fields. + * + * Example: + * ```c++ + * struct Car { + * std::string make; + * std::string model; + * int year; + * double price; + * }; + * + * Car car; + * object.extract_into<"make", "model">(car); + * // Only 'make' and 'model' fields are extracted from JSON + * ``` + * + * @tparam FieldNames Compile-time string literals specifying which fields to extract + * @param out The output struct to populate with selected fields + * @returns SUCCESS on success, or an error code if a required field is missing or has wrong type + */ + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept; +#endif // SIMDJSON_STATIC_REFLECTION +#endif // SIMDJSON_SUPPORTS_CONCEPTS protected: /** * Go to the end of the object, no matter where you are right now. */ - simdjson_inline error_code consume() noexcept; + simdjson_warn_unused simdjson_inline error_code consume() noexcept; static simdjson_inline simdjson_result start(value_iterator &iter) noexcept; static simdjson_inline simdjson_result start_root(value_iterator &iter) noexcept; static simdjson_inline simdjson_result started(value_iterator &iter) noexcept; @@ -47991,12 +52142,42 @@ struct simdjson_result : public fallback::implementa simdjson_inline simdjson_result operator[](std::string_view key) && noexcept; simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; - inline simdjson_result reset() noexcept; inline simdjson_result is_empty() noexcept; inline simdjson_result count_fields() & noexcept; inline simdjson_result raw_json() noexcept; + #if SIMDJSON_SUPPORTS_CONCEPTS + // TODO: move this code into object-inl.h + template + simdjson_inline simdjson_result get() noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + return first; + } + return first.get(); + } + template + simdjson_warn_unused simdjson_inline error_code get(T& out) noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + out = first; + } else { + SIMDJSON_TRY( first.get(out) ); + } + return SUCCESS; + } + +#if SIMDJSON_STATIC_REFLECTION + // TODO: move this code into object-inl.h + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) noexcept { + if (error()) { return error(); } + return first.extract_into(out); + } +#endif // SIMDJSON_STATIC_REFLECTION +#endif // SIMDJSON_SUPPORTS_CONCEPTS }; } // namespace simdjson @@ -48125,6 +52306,20 @@ inline simdjson_result to_json_string(simdjson_result to_json_string(simdjson_result x); inline simdjson_result to_json_string(simdjson_result x); inline simdjson_result to_json_string(simdjson_result x); + +#if SIMDJSON_STATIC_REFLECTION +/** + * Create a JSON string from any user-defined type using static reflection. + * Only available when SIMDJSON_STATIC_REFLECTION is enabled. + */ +template + requires(!std::same_as && + !std::same_as && + !std::same_as && + !std::same_as) +inline std::string to_json_string(const T& obj); +#endif + } // namespace simdjson /** @@ -48196,28 +52391,30 @@ inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result #include +#if SIMDJSON_STATIC_REFLECTION +#include +// #include // for std::define_static_string - header not available yet +#endif namespace simdjson { -template -constexpr bool require_custom_serialization = false; ////////////////////////////// // Number deserialization ////////////////////////////// template - requires(!require_custom_serialization) error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { using limits = std::numeric_limits; @@ -48231,7 +52428,6 @@ error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { } template - requires(!require_custom_serialization) error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { double x; SIMDJSON_TRY(val.get_double().get(x)); @@ -48240,7 +52436,6 @@ error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { } template - requires(!require_custom_serialization) error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { using limits = std::numeric_limits; @@ -48253,8 +52448,23 @@ error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { return SUCCESS; } +////////////////////////////// +// String deserialization +////////////////////////////// + +// just a character! +error_code tag_invoke(deserialize_tag, auto &val, char &out) noexcept { + std::string_view x; + SIMDJSON_TRY(val.get_string().get(x)); + if(x.size() != 1) { + return INCORRECT_TYPE; + } + out = x[0]; + return SUCCESS; +} + +// any string-like type (can be constructed from std::string_view) template - requires(!require_custom_serialization) error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(std::is_nothrow_constructible_v) { std::string_view str; SIMDJSON_TRY(val.get_string().get(str)); @@ -48271,7 +52481,6 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(std::is_nothr * doc.get>(). */ template - requires(!require_custom_serialization) error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { using value_type = typename std::remove_cvref_t::value_type; static_assert( @@ -48280,9 +52489,13 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { static_assert( std::is_default_constructible_v, "The specified type inside the container must default constructible."); - fallback::ondemand::array arr; - SIMDJSON_TRY(val.get_array().get(arr)); + if constexpr (std::is_same_v, fallback::ondemand::array>) { + arr = val; + } else { + SIMDJSON_TRY(val.get_array().get(arr)); + } + for (auto v : arr) { if constexpr (concepts::returns_reference) { if (auto const err = v.get().get(concepts::emplace_one(out)); @@ -48313,7 +52526,6 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { * string-keyed types. */ template - requires(!require_custom_serialization) error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { using value_type = typename std::remove_cvref_t::mapped_type; static_assert( @@ -48339,7 +52551,45 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { return SUCCESS; } +template +error_code tag_invoke(deserialize_tag, fallback::ondemand::object &obj, T &out) noexcept { + using value_type = typename std::remove_cvref_t::mapped_type; + out.clear(); + for (auto field : obj) { + std::string_view key; + SIMDJSON_TRY(field.unescaped_key().get(key)); + + fallback::ondemand::value value_obj; + SIMDJSON_TRY(field.value().get(value_obj)); + + value_type this_value; + SIMDJSON_TRY(value_obj.get(this_value)); + out.emplace(typename T::key_type(key), std::move(this_value)); + } + return SUCCESS; +} + +template +error_code tag_invoke(deserialize_tag, fallback::ondemand::value &val, T &out) noexcept { + fallback::ondemand::object obj; + SIMDJSON_TRY(val.get_object().get(obj)); + return simdjson::deserialize(obj, out); +} + +template +error_code tag_invoke(deserialize_tag, fallback::ondemand::document &doc, T &out) noexcept { + fallback::ondemand::object obj; + SIMDJSON_TRY(doc.get_object().get(obj)); + return simdjson::deserialize(obj, out); +} + +template +error_code tag_invoke(deserialize_tag, fallback::ondemand::document_reference &doc, T &out) noexcept { + fallback::ondemand::object obj; + SIMDJSON_TRY(doc.get_object().get(obj)); + return simdjson::deserialize(obj, out); +} /** @@ -48357,7 +52607,6 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { * @return status of the conversion */ template - requires(!require_custom_serialization) error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deserializable::element_type, ValT>) { using element_type = typename std::remove_cvref_t::element_type; @@ -48382,17 +52631,17 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deser /** * This CPO (Customization Point Object) will help deserialize into optional types. */ -template - requires(!require_custom_serialization) -error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deserializable::value_type, ValT>) { +template +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept(nothrow_deserializable::value_type, decltype(val)>) { using value_type = typename std::remove_cvref_t::value_type; - static_assert( - deserializable, - "The specified type inside the unique_ptr must itself be deserializable"); - static_assert( - std::is_default_constructible_v, - "The specified type inside the unique_ptr must default constructible."); + // Check if the value is null + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); // Set to nullopt + return SUCCESS; + } if (!out) { out.emplace(); @@ -48401,10 +52650,329 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deser return SUCCESS; } + +#if SIMDJSON_STATIC_REFLECTION + + +template +constexpr bool user_defined_type = (std::is_class_v +&& !std::is_same_v && !std::is_same_v && !concepts::optional_type && +!concepts::appendable_containers); + + +template + requires(user_defined_type && std::is_class_v) +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept { + fallback::ondemand::object obj; + if constexpr (std::is_same_v, fallback::ondemand::object>) { + obj = val; + } else { + SIMDJSON_TRY(val.get_object().get(obj)); + } + template for (constexpr auto mem : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) { + if constexpr (!std::meta::is_const(mem) && std::meta::is_public(mem)) { + constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem)); + if constexpr (concepts::optional_type) { + // for optional members, it's ok if the key is missing + auto error = obj[key].get(out.[:mem:]); + if (error && error != NO_SUCH_FIELD) { + if(error == NO_SUCH_FIELD) { + out.[:mem:].reset(); + continue; + } + return error; + } + } else { + // for non-optional members, the key must be present + SIMDJSON_TRY(obj[key].get(out.[:mem:])); + } + } + }; + return simdjson::SUCCESS; +} + +// Support for enum deserialization - deserialize from string representation using expand approach from P2996R12 +template + requires(std::is_enum_v) +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept { +#if SIMDJSON_STATIC_REFLECTION + std::string_view str; + SIMDJSON_TRY(val.get_string().get(str)); + constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T)); + template for (constexpr auto enum_val : enumerators) { + if (str == std::meta::identifier_of(enum_val)) { + out = [:enum_val:]; + return SUCCESS; + } + }; + + return INCORRECT_TYPE; +#else + // Fallback: deserialize as integer if reflection not available + std::underlying_type_t int_val; + SIMDJSON_TRY(val.get(int_val)); + out = static_cast(int_val); + return SUCCESS; +#endif +} + +template + requires(user_defined_type>) +error_code tag_invoke(deserialize_tag, simdjson_value &val, std::unique_ptr &out) noexcept { + if (!out) { + out = std::make_unique(); + if (!out) { + return MEMALLOC; + } + } + if (auto err = val.get(*out)) { + out.reset(); + return err; + } + return SUCCESS; +} + +template + requires(user_defined_type>) +error_code tag_invoke(deserialize_tag, simdjson_value &val, std::shared_ptr &out) noexcept { + if (!out) { + out = std::make_shared(); + if (!out) { + return MEMALLOC; + } + } + if (auto err = val.get(*out)) { + out.reset(); + return err; + } + return SUCCESS; +} + +#endif // SIMDJSON_STATIC_REFLECTION + +//////////////////////////////////////// +// Unique pointers +//////////////////////////////////////// +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_unique(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_bool().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_unique(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_int64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_unique(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_uint64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_unique(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_double().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_unique(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_string().get(*out)); + return SUCCESS; +} + + +//////////////////////////////////////// +// Shared pointers +//////////////////////////////////////// +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_shared(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_bool().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_shared(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_int64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_shared(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_uint64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_shared(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_double().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_shared(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_string().get(*out)); + return SUCCESS; +} + + +//////////////////////////////////////// +// Explicit optional specializations +//////////////////////////////////////// + +//////////////////////////////////////// +// Explicit smart pointer specializations for string and int types +//////////////////////////////////////// +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + // Check if the value is null + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); // Set to nullptr + return SUCCESS; + } + + if (!out) { + out = std::make_unique(); + } + std::string_view str; + SIMDJSON_TRY(val.get_string().get(str)); + *out = std::string{str}; + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + // Check if the value is null + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); // Set to nullptr + return SUCCESS; + } + + if (!out) { + out = std::make_shared(); + } + std::string_view str; + SIMDJSON_TRY(val.get_string().get(str)); + *out = std::string{str}; + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + // Check if the value is null + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); // Set to nullptr + return SUCCESS; + } + + if (!out) { + out = std::make_unique(); + } + int64_t temp; + SIMDJSON_TRY(val.get_int64().get(temp)); + *out = static_cast(temp); + return SUCCESS; +} + } // namespace simdjson #endif // SIMDJSON_ONDEMAND_DESERIALIZE_H -#endif // SIMDJSON_SUPPORTS_DESERIALIZATION +#endif // SIMDJSON_SUPPORTS_CONCEPTS /* end file simdjson/generic/ondemand/std_deserialize.h for fallback */ // Inline definitions @@ -48497,7 +53065,7 @@ simdjson_inline simdjson_result array::begin() noexcept { simdjson_inline simdjson_result array::end() noexcept { return array_iterator(iter); } -simdjson_inline error_code array::consume() noexcept { +simdjson_warn_unused simdjson_warn_unused simdjson_inline error_code array::consume() noexcept { auto error = iter.json_iter().skip_child(iter.depth()-1); if(error) { iter.abandon(); } return error; @@ -48688,6 +53256,9 @@ simdjson_inline array_iterator &array_iterator::operator++() noexcept { return *this; } +simdjson_inline bool array_iterator::at_end() const noexcept { + return iter.at_end(); +} } // namespace ondemand } // namespace fallback } // namespace simdjson @@ -48724,7 +53295,9 @@ simdjson_inline simdjson_result &simdjson_re ++(first); return *this; } - +simdjson_inline bool simdjson_result::at_end() const noexcept { + return !first.iter.is_valid() || first.at_end(); +} } // namespace simdjson #endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_INL_H @@ -48781,7 +53354,7 @@ simdjson_inline simdjson_result value::get_string(bool allow_r return iter.get_string(allow_replacement); } template -simdjson_inline error_code value::get_string(string_type& receiver, bool allow_replacement) noexcept { +simdjson_warn_unused simdjson_inline error_code value::get_string(string_type& receiver, bool allow_replacement) noexcept { return iter.get_string(receiver, allow_replacement); } simdjson_inline simdjson_result value::get_wobbly_string() noexcept { @@ -48823,15 +53396,15 @@ template<> simdjson_inline simdjson_result value::get() noexcept { retu template<> simdjson_inline simdjson_result value::get() noexcept { return get_bool(); } -template<> simdjson_inline error_code value::get(array& out) noexcept { return get_array().get(out); } -template<> simdjson_inline error_code value::get(object& out) noexcept { return get_object().get(out); } -template<> simdjson_inline error_code value::get(raw_json_string& out) noexcept { return get_raw_json_string().get(out); } -template<> simdjson_inline error_code value::get(std::string_view& out) noexcept { return get_string(false).get(out); } -template<> simdjson_inline error_code value::get(number& out) noexcept { return get_number().get(out); } -template<> simdjson_inline error_code value::get(double& out) noexcept { return get_double().get(out); } -template<> simdjson_inline error_code value::get(uint64_t& out) noexcept { return get_uint64().get(out); } -template<> simdjson_inline error_code value::get(int64_t& out) noexcept { return get_int64().get(out); } -template<> simdjson_inline error_code value::get(bool& out) noexcept { return get_bool().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(array& out) noexcept { return get_array().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(object& out) noexcept { return get_object().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(raw_json_string& out) noexcept { return get_raw_json_string().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(std::string_view& out) noexcept { return get_string(false).get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(number& out) noexcept { return get_number().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(double& out) noexcept { return get_double().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(uint64_t& out) noexcept { return get_uint64().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(int64_t& out) noexcept { return get_int64().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(bool& out) noexcept { return get_bool().get(out); } #if SIMDJSON_EXCEPTIONS template @@ -49428,7 +54001,7 @@ simdjson_inline simdjson_result document::get_string(bool allo return get_root_value_iterator().get_root_string(true, allow_replacement); } template -simdjson_inline error_code document::get_string(string_type& receiver, bool allow_replacement) noexcept { +simdjson_warn_unused simdjson_inline error_code document::get_string(string_type& receiver, bool allow_replacement) noexcept { return get_root_value_iterator().get_root_string(receiver, true, allow_replacement); } simdjson_inline simdjson_result document::get_wobbly_string() noexcept { @@ -49454,15 +54027,15 @@ template<> simdjson_inline simdjson_result document::get() & noexcept { template<> simdjson_inline simdjson_result document::get() & noexcept { return get_bool(); } template<> simdjson_inline simdjson_result document::get() & noexcept { return get_value(); } -template<> simdjson_inline error_code document::get(array& out) & noexcept { return get_array().get(out); } -template<> simdjson_inline error_code document::get(object& out) & noexcept { return get_object().get(out); } -template<> simdjson_inline error_code document::get(raw_json_string& out) & noexcept { return get_raw_json_string().get(out); } -template<> simdjson_inline error_code document::get(std::string_view& out) & noexcept { return get_string(false).get(out); } -template<> simdjson_inline error_code document::get(double& out) & noexcept { return get_double().get(out); } -template<> simdjson_inline error_code document::get(uint64_t& out) & noexcept { return get_uint64().get(out); } -template<> simdjson_inline error_code document::get(int64_t& out) & noexcept { return get_int64().get(out); } -template<> simdjson_inline error_code document::get(bool& out) & noexcept { return get_bool().get(out); } -template<> simdjson_inline error_code document::get(value& out) & noexcept { return get_value().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(array& out) & noexcept { return get_array().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(object& out) & noexcept { return get_object().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(raw_json_string& out) & noexcept { return get_raw_json_string().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(std::string_view& out) & noexcept { return get_string(false).get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(double& out) & noexcept { return get_double().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(uint64_t& out) & noexcept { return get_uint64().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(int64_t& out) & noexcept { return get_int64().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(bool& out) & noexcept { return get_bool().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(value& out) & noexcept { return get_value().get(out); } template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_raw_json_string(); } template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_string(false); } @@ -49482,8 +54055,8 @@ simdjson_inline document::operator object() & noexcept(false) { return get_objec simdjson_inline document::operator uint64_t() noexcept(false) { return get_uint64(); } simdjson_inline document::operator int64_t() noexcept(false) { return get_int64(); } simdjson_inline document::operator double() noexcept(false) { return get_double(); } -simdjson_inline document::operator std::string_view() noexcept(false) { return get_string(false); } -simdjson_inline document::operator raw_json_string() noexcept(false) { return get_raw_json_string(); } +simdjson_inline document::operator std::string_view() noexcept(false) simdjson_lifetime_bound { return get_string(false); } +simdjson_inline document::operator raw_json_string() noexcept(false) simdjson_lifetime_bound { return get_raw_json_string(); } simdjson_inline document::operator bool() noexcept(false) { return get_bool(); } simdjson_inline document::operator value() noexcept(false) { return get_value(); } @@ -49532,7 +54105,7 @@ simdjson_inline simdjson_result document::operator[](const char *key) & n return start_or_resume_object()[key]; } -simdjson_inline error_code document::consume() noexcept { +simdjson_warn_unused simdjson_inline error_code document::consume() noexcept { bool scalar = false; auto error = is_scalar().get(scalar); if(error) { return error; } @@ -49634,6 +54207,54 @@ simdjson_inline simdjson_result document::at_path(std::string_view json_p } } + + +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION + +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code document::extract_into(T& out) & noexcept { + // Helper to check if a field name matches any of the requested fields + auto should_extract = [](std::string_view field_name) constexpr -> bool { + return ((FieldNames.view() == field_name) || ...); + }; + + // Iterate through all members of T using reflection + template for (constexpr auto mem : std::define_static_array( + std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) { + + if constexpr (!std::meta::is_const(mem) && std::meta::is_public(mem)) { + constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem)); + + // Only extract this field if it's in our list of requested fields + if constexpr (should_extract(key)) { + // Try to find and extract the field + if constexpr (concepts::optional_type) { + // For optional fields, it's ok if they're missing + auto field_result = find_field_unordered(key); + if (!field_result.error()) { + auto error = field_result.get(out.[:mem:]); + if (error && error != NO_SUCH_FIELD) { + return error; + } + } else if (field_result.error() != NO_SUCH_FIELD) { + return field_result.error(); + } else { + out.[:mem:].reset(); + } + } else { + // For required fields (in the requested list), fail if missing + SIMDJSON_TRY((*this)[key].get(out.[:mem:])); + } + } + } + }; + + return SUCCESS; +} + +#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION + } // namespace ondemand } // namespace fallback } // namespace simdjson @@ -49741,7 +54362,7 @@ simdjson_inline simdjson_result simdjson_result -simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { if (error()) { return error(); } return first.get_string(receiver, allow_replacement); } @@ -49777,12 +54398,12 @@ simdjson_deprecated simdjson_inline simdjson_result simdjson_result(first).get(); } template -simdjson_inline error_code simdjson_result::get(T &out) & noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) & noexcept { if (error()) { return error(); } return first.get(out); } template -simdjson_inline error_code simdjson_result::get(T &out) && noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) && noexcept { if (error()) { return error(); } return std::forward(first).get(out); } @@ -49792,8 +54413,8 @@ template<> simdjson_deprecated simdjson_inline simdjson_result(first); } -template<> simdjson_inline error_code simdjson_result::get(fallback::ondemand::document &out) & noexcept = delete; -template<> simdjson_inline error_code simdjson_result::get(fallback::ondemand::document &out) && noexcept { +template<> simdjson_warn_unused simdjson_inline error_code simdjson_result::get(fallback::ondemand::document &out) & noexcept = delete; +template<> simdjson_warn_unused simdjson_inline error_code simdjson_result::get(fallback::ondemand::document &out) && noexcept { if (error()) { return error(); } out = std::forward(first); return SUCCESS; @@ -49911,6 +54532,15 @@ simdjson_inline simdjson_result simdjson_result + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code simdjson_result::extract_into(T& out) & noexcept { + if (error()) { return error(); } + return first.extract_into(out); +} +#endif // SIMDJSON_STATIC_REFLECTION + } // namespace simdjson @@ -49945,7 +54575,7 @@ simdjson_inline simdjson_result document_reference::get_double() noexcep simdjson_inline simdjson_result document_reference::get_double_in_string() noexcept { return doc->get_root_value_iterator().get_root_double(false); } simdjson_inline simdjson_result document_reference::get_string(bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(false, allow_replacement); } template -simdjson_inline error_code document_reference::get_string(string_type& receiver, bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(receiver, false, allow_replacement); } +simdjson_warn_unused simdjson_inline error_code document_reference::get_string(string_type& receiver, bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(receiver, false, allow_replacement); } simdjson_inline simdjson_result document_reference::get_wobbly_string() noexcept { return doc->get_root_value_iterator().get_root_wobbly_string(false); } simdjson_inline simdjson_result document_reference::get_raw_json_string() noexcept { return doc->get_root_value_iterator().get_root_raw_json_string(false); } simdjson_inline simdjson_result document_reference::get_bool() noexcept { return doc->get_root_value_iterator().get_root_bool(false); } @@ -49998,7 +54628,13 @@ simdjson_inline simdjson_result document_reference::at_pointer(std::strin simdjson_inline simdjson_result document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); } simdjson_inline simdjson_result document_reference::raw_json() noexcept { return doc->raw_json();} simdjson_inline document_reference::operator document&() const noexcept { return *doc; } - +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code document_reference::extract_into(T& out) & noexcept { + return doc->extract_into(out); +} +#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION } // namespace ondemand } // namespace fallback } // namespace simdjson @@ -50095,7 +54731,7 @@ simdjson_inline simdjson_result simdjson_result -simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { if (error()) { return error(); } return first.get_string(receiver, allow_replacement); } @@ -50130,12 +54766,12 @@ simdjson_inline simdjson_result simdjson_result(first).get(); } template -simdjson_inline error_code simdjson_result::get(T &out) & noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) & noexcept { if (error()) { return error(); } return first.get(out); } template -simdjson_inline error_code simdjson_result::get(T &out) && noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) && noexcept { if (error()) { return error(); } return std::forward(first).get(out); } @@ -50152,13 +54788,13 @@ simdjson_inline simdjson_result simdjson_result -simdjson_inline error_code simdjson_result::get(fallback::ondemand::document_reference &out) & noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(fallback::ondemand::document_reference &out) & noexcept { if (error()) { return error(); } out = first; return SUCCESS; } template <> -simdjson_inline error_code simdjson_result::get(fallback::ondemand::document_reference &out) && noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(fallback::ondemand::document_reference &out) && noexcept { if (error()) { return error(); } out = first; return SUCCESS; @@ -50246,7 +54882,14 @@ simdjson_inline simdjson_result simdjson_result + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code simdjson_result::extract_into(T& out) & noexcept { + if (error()) { return error(); } + return first.extract_into(out); +} +#endif // SIMDJSON_STATIC_REFLECTION } // namespace simdjson #endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H @@ -50437,10 +55080,19 @@ simdjson_inline document_stream::iterator& document_stream::iterator::operator++ return *this; } +simdjson_inline bool document_stream::iterator::at_end() const noexcept { + return finished; +} + + simdjson_inline bool document_stream::iterator::operator!=(const document_stream::iterator &other) const noexcept { return finished != other.finished; } +simdjson_inline bool document_stream::iterator::operator==(const document_stream::iterator &other) const noexcept { + return finished == other.finished; +} + simdjson_inline document_stream::iterator document_stream::begin() noexcept { start(); // If there are no documents, we're finished. @@ -50558,7 +55210,10 @@ inline void document_stream::next_document() noexcept { // Always set depth=1 at the start of document doc.iter._depth = 1; // consume comma if comma separated is allowed - if (allow_comma_separated) { doc.iter.consume_character(','); } + if (allow_comma_separated) { + error_code ignored = doc.iter.consume_character(','); + static_cast(ignored); // ignored on purpose + } // Resets the string buffer at the beginning, thus invalidating the strings. doc.iter._string_buf_loc = parser->string_buf.get(); doc.iter._root = doc.iter.position(); @@ -50804,7 +55459,7 @@ simdjson_inline simdjson_result simdjson_result -simdjson_inline error_code simdjson_result::unescaped_key(string_type &receiver, bool allow_replacement) noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::unescaped_key(string_type &receiver, bool allow_replacement) noexcept { if (error()) { return error(); } return first.unescaped_key(receiver, allow_replacement); } @@ -51040,6 +55695,8 @@ simdjson_inline void json_iterator::assert_valid_position(token_position positio #ifndef SIMDJSON_CLANG_VISUAL_STUDIO SIMDJSON_ASSUME( position >= &parser->implementation->structural_indexes[0] ); SIMDJSON_ASSUME( position < &parser->implementation->structural_indexes[parser->implementation->n_structural_indexes] ); +#else + (void)position; // Suppress unused parameter warning #endif } @@ -51164,7 +55821,7 @@ simdjson_inline uint8_t *&json_iterator::string_buf_loc() noexcept { return _string_buf_loc; } -simdjson_inline error_code json_iterator::report_error(error_code _error, const char *message) noexcept { +simdjson_warn_unused simdjson_inline error_code json_iterator::report_error(error_code _error, const char *message) noexcept { SIMDJSON_ASSUME(_error != SUCCESS && _error != UNINITIALIZED && _error != INCORRECT_TYPE && _error != NO_SUCH_FIELD); logger::log_error(*this, message); error = _error; @@ -51208,7 +55865,7 @@ simdjson_inline void json_iterator::reenter_child(token_position position, depth _depth = child_depth; } -simdjson_inline error_code json_iterator::consume_character(char c) noexcept { +simdjson_warn_unused simdjson_inline error_code json_iterator::consume_character(char c) noexcept { if (*peek() == c) { return_current_and_advance(); return SUCCESS; @@ -51231,7 +55888,7 @@ simdjson_inline void json_iterator::set_start_position(depth_t depth, token_posi #endif -simdjson_inline error_code json_iterator::optional_error(error_code _error, const char *message) noexcept { +simdjson_warn_unused simdjson_inline error_code json_iterator::optional_error(error_code _error, const char *message) noexcept { SIMDJSON_ASSUME(_error == INCORRECT_TYPE || _error == NO_SUCH_FIELD); logger::log_error(*this, message); return _error; @@ -51626,6 +56283,10 @@ inline void log_line(const json_iterator &iter, token_position index, depth_t de /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value-inl.h" */ +/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_string_builder.h" // for constevalutil::fixed_string */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #endif */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { @@ -51683,7 +56344,7 @@ simdjson_inline simdjson_result object::start_root(value_iterator &iter) SIMDJSON_TRY( iter.start_root_object().error() ); return object(iter); } -simdjson_inline error_code object::consume() noexcept { +simdjson_warn_unused simdjson_inline error_code object::consume() noexcept { if(iter.is_at_key()) { /** * whenever you are pointing at a key, calling skip_child() is @@ -51812,6 +56473,52 @@ simdjson_inline simdjson_result object::reset() & noexcept { return iter.reset_object(); } +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION + +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code object::extract_into(T& out) & noexcept { + // Helper to check if a field name matches any of the requested fields + auto should_extract = [](std::string_view field_name) constexpr -> bool { + return ((FieldNames.view() == field_name) || ...); + }; + + // Iterate through all members of T using reflection + template for (constexpr auto mem : std::define_static_array( + std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) { + + if constexpr (!std::meta::is_const(mem) && std::meta::is_public(mem)) { + constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem)); + + // Only extract this field if it's in our list of requested fields + if constexpr (should_extract(key)) { + // Try to find and extract the field + if constexpr (concepts::optional_type) { + // For optional fields, it's ok if they're missing + auto field_result = find_field_unordered(key); + if (!field_result.error()) { + auto error = field_result.get(out.[:mem:]); + if (error && error != NO_SUCH_FIELD) { + return error; + } + } else if (field_result.error() != NO_SUCH_FIELD) { + return field_result.error(); + } else { + out.[:mem:].reset(); + } + } else { + // For required fields (in the requested list), fail if missing + SIMDJSON_TRY((*this)[key].get(out.[:mem:])); + } + } + } + }; + + return SUCCESS; +} + +#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION + } // namespace ondemand } // namespace fallback } // namespace simdjson @@ -51888,6 +56595,7 @@ simdjson_inline simdjson_result simdjson_result parser::iterate(padded_string_view json) & noexcept { - if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; } + if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; } json.remove_utf8_bom(); @@ -52102,7 +56810,7 @@ simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(p #ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON simdjson_warn_unused simdjson_inline simdjson_result parser::iterate_allow_incomplete_json(padded_string_view json) & noexcept { - if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; } + if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; } json.remove_utf8_bom(); @@ -52134,10 +56842,7 @@ simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(s } simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(std::string &json) & noexcept { - if(json.capacity() - json.size() < SIMDJSON_PADDING) { - json.reserve(json.size() + SIMDJSON_PADDING); - } - return iterate(padded_string_view(json)); + return iterate(pad_with_reserve(json)); } simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(const std::string &json) & noexcept { @@ -52159,7 +56864,7 @@ simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(c } simdjson_warn_unused simdjson_inline simdjson_result parser::iterate_raw(padded_string_view json) & noexcept { - if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; } + if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; } json.remove_utf8_bom(); @@ -52174,6 +56879,7 @@ simdjson_warn_unused simdjson_inline simdjson_result parser::iter } inline simdjson_result parser::iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept { + // Warning: no check is done on the buffer padding. We trust the user. if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; } if((len >= 3) && (std::memcmp(buf, "\xEF\xBB\xBF", 3) == 0)) { buf += 3; @@ -52182,16 +56888,24 @@ inline simdjson_result parser::iterate_many(const uint8_t *buf, if(allow_comma_separated && batch_size < len) { batch_size = len; } return document_stream(*this, buf, len, batch_size, allow_comma_separated); } + inline simdjson_result parser::iterate_many(const char *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept { + // Warning: no check is done on the buffer padding. We trust the user. return iterate_many(reinterpret_cast(buf), len, batch_size, allow_comma_separated); } -inline simdjson_result parser::iterate_many(const std::string &s, size_t batch_size, bool allow_comma_separated) noexcept { +inline simdjson_result parser::iterate_many(padded_string_view s, size_t batch_size, bool allow_comma_separated) noexcept { + if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; } return iterate_many(s.data(), s.length(), batch_size, allow_comma_separated); } inline simdjson_result parser::iterate_many(const padded_string &s, size_t batch_size, bool allow_comma_separated) noexcept { - return iterate_many(s.data(), s.length(), batch_size, allow_comma_separated); + return iterate_many(padded_string_view(s), batch_size, allow_comma_separated); +} +inline simdjson_result parser::iterate_many(const std::string &s, size_t batch_size, bool allow_comma_separated) noexcept { + return iterate_many(padded_string_view(s), batch_size, allow_comma_separated); +} +inline simdjson_result parser::iterate_many(std::string &s, size_t batch_size, bool allow_comma_separated) noexcept { + return iterate_many(pad(s), batch_size, allow_comma_separated); } - simdjson_pure simdjson_inline size_t parser::capacity() const noexcept { return _capacity; } @@ -52226,6 +56940,34 @@ simdjson_inline simdjson_warn_unused simdjson_result parser::u return result; } +simdjson_inline simdjson_warn_unused ondemand::parser& parser::get_parser() { + return *parser::get_parser_instance(); +} + +simdjson_inline bool release_parser() { + auto &parser_instance = parser::get_threadlocal_parser_if_exists(); + if (parser_instance) { + parser_instance.reset(); + return true; + } + return false; +} + +simdjson_inline simdjson_warn_unused std::unique_ptr& parser::get_parser_instance() { + std::unique_ptr& parser_instance = get_threadlocal_parser_if_exists(); + if (!parser_instance) { + parser_instance.reset(new ondemand::parser()); + } + return parser_instance; +} + +simdjson_inline simdjson_warn_unused std::unique_ptr& parser::get_threadlocal_parser_if_exists() { + // @the-moisrex points out that this could be implemented with std::optional (C++17). + thread_local std::unique_ptr parser_instance = nullptr; + return parser_instance; +} + + } // namespace ondemand } // namespace fallback } // namespace simdjson @@ -52260,8 +57002,13 @@ namespace ondemand { simdjson_inline raw_json_string::raw_json_string(const uint8_t * _buf) noexcept : buf{_buf} {} -simdjson_inline const char * raw_json_string::raw() const noexcept { return reinterpret_cast(buf); } +simdjson_inline const char * raw_json_string::raw() const noexcept { + return reinterpret_cast(buf); +} +simdjson_inline char raw_json_string::operator[](size_t i) const noexcept { + return reinterpret_cast(buf)[i]; +} simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(std::string_view target) noexcept { size_t pos{0}; @@ -52438,6 +57185,10 @@ simdjson_inline simdjson_result simdjson_result::operator[](size_t i) const noexcept { + if (error()) { return error(); } + return first[i]; +} simdjson_inline simdjson_warn_unused simdjson_result simdjson_result::unescape(fallback::ondemand::json_iterator &iter, bool allow_replacement) const noexcept { if (error()) { return error(); } return first.unescape(iter, allow_replacement); @@ -52463,6 +57214,9 @@ simdjson_inline simdjson_warn_unused simdjson_result simdjson_ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/serialization.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */ +/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_builder.h" */ +/* amalgamation skipped (editor-only): #endif */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { @@ -52828,7 +57582,7 @@ simdjson_warn_unused simdjson_inline simdjson_result value_iterator::start if (*_json_iter->peek() == '}') { logger::log_value(*_json_iter, "empty object"); _json_iter->return_current_and_advance(); - end_container(); + SIMDJSON_TRY(end_container()); return false; } return true; @@ -53682,7 +58436,7 @@ simdjson_inline void value_iterator::advance_scalar(const char *type) noexcept { _json_iter->ascend_to(depth()-1); } -simdjson_inline error_code value_iterator::start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept { +simdjson_warn_unused simdjson_inline error_code value_iterator::start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept { logger::log_start_value(*_json_iter, start_position(), depth(), type); // If we're not at the position anymore, we don't want to advance the cursor. const uint8_t *json; @@ -53844,7 +58598,7 @@ simdjson_inline simdjson_result value_iterator::type() const noexcept case '5': case '6': case '7': case '8': case '9': return json_type::number; default: - return TAPE_ERROR; + return json_type::unknown; } } @@ -53884,6 +58638,1097 @@ simdjson_inline simdjson_result::simdjson_re #endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H /* end file simdjson/generic/ondemand/value_iterator-inl.h for fallback */ +// JSON builder inline definitions +/* including simdjson/generic/ondemand/json_string_builder-inl.h for fallback: #include "simdjson/generic/ondemand/json_string_builder-inl.h" */ +/* begin file simdjson/generic/ondemand/json_string_builder-inl.h for fallback */ +/** + * This file is part of the builder API. It is temporarily in the ondemand + * directory but we will move it to a builder directory later. + */ +#include +#include +#include +#ifndef SIMDJSON_GENERIC_STRING_BUILDER_INL_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_INL_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_string_builder.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +/* + * Empirically, we have found that an inlined optimization is important for + * performance. The following macros are not ideal. We should find a better + * way to inline the code. + */ + +#if defined(__SSE2__) || defined(__x86_64__) || defined(__x86_64) || \ + (defined(_M_AMD64) || defined(_M_X64) || \ + (defined(_M_IX86_FP) && _M_IX86_FP == 2)) +#ifndef SIMDJSON_EXPERIMENTAL_HAS_SSE2 +#define SIMDJSON_EXPERIMENTAL_HAS_SSE2 1 +#endif +#endif + +#if defined(__aarch64__) || defined(_M_ARM64) +#ifndef SIMDJSON_EXPERIMENTAL_HAS_NEON +#define SIMDJSON_EXPERIMENTAL_HAS_NEON 1 +#endif +#endif +#if SIMDJSON_EXPERIMENTAL_HAS_NEON +#include +#endif +#if SIMDJSON_EXPERIMENTAL_HAS_SSE2 +#include +#endif + +namespace simdjson { +namespace fallback { +namespace builder { + +static SIMDJSON_CONSTEXPR_LAMBDA std::array + json_quotable_character = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 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, 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, 1, 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, 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, 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, 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, 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, 0, 0, 0}; + +/** + +A possible SWAR implementation of has_json_escapable_byte. It is not used +because it is slower than the current implementation. It is kept here for +reference (to show that we tried it). + +inline bool has_json_escapable_byte(uint64_t x) { + uint64_t is_ascii = 0x8080808080808080ULL & ~x; + uint64_t xor2 = x ^ 0x0202020202020202ULL; + uint64_t lt32_or_eq34 = xor2 - 0x2121212121212121ULL; + uint64_t sub92 = x ^ 0x5C5C5C5C5C5C5C5CULL; + uint64_t eq92 = (sub92 - 0x0101010101010101ULL); + return ((lt32_or_eq34 | eq92) & is_ascii) != 0; +} + +**/ + +SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline bool +simple_needs_escaping(std::string_view v) { + for (char c : v) { + // a table lookup is faster than a series of comparisons + if (json_quotable_character[static_cast(c)]) { + return true; + } + } + return false; +} + +#if SIMDJSON_EXPERIMENTAL_HAS_NEON +simdjson_inline bool fast_needs_escaping(std::string_view view) { + if (view.size() < 16) { + return simple_needs_escaping(view); + } + size_t i = 0; + uint8x16_t running = vdupq_n_u8(0); + uint8x16_t v34 = vdupq_n_u8(34); + uint8x16_t v92 = vdupq_n_u8(92); + + for (; i + 15 < view.size(); i += 16) { + uint8x16_t word = vld1q_u8((const uint8_t *)view.data() + i); + running = vorrq_u8(running, vceqq_u8(word, v34)); + running = vorrq_u8(running, vceqq_u8(word, v92)); + running = vorrq_u8(running, vcltq_u8(word, vdupq_n_u8(32))); + } + if (i < view.size()) { + uint8x16_t word = + vld1q_u8((const uint8_t *)view.data() + view.length() - 16); + running = vorrq_u8(running, vceqq_u8(word, v34)); + running = vorrq_u8(running, vceqq_u8(word, v92)); + running = vorrq_u8(running, vcltq_u8(word, vdupq_n_u8(32))); + } + return vmaxvq_u32(vreinterpretq_u32_u8(running)) != 0; +} +#elif SIMDJSON_EXPERIMENTAL_HAS_SSE2 +simdjson_inline bool fast_needs_escaping(std::string_view view) { + if (view.size() < 16) { + return simple_needs_escaping(view); + } + size_t i = 0; + __m128i running = _mm_setzero_si128(); + for (; i + 15 < view.size(); i += 16) { + + __m128i word = + _mm_loadu_si128(reinterpret_cast(view.data() + i)); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(34))); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(92))); + running = _mm_or_si128( + running, _mm_cmpeq_epi8(_mm_subs_epu8(word, _mm_set1_epi8(31)), + _mm_setzero_si128())); + } + if (i < view.size()) { + __m128i word = _mm_loadu_si128( + reinterpret_cast(view.data() + view.length() - 16)); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(34))); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(92))); + running = _mm_or_si128( + running, _mm_cmpeq_epi8(_mm_subs_epu8(word, _mm_set1_epi8(31)), + _mm_setzero_si128())); + } + return _mm_movemask_epi8(running) != 0; +} +#else +simdjson_inline bool fast_needs_escaping(std::string_view view) { + return simple_needs_escaping(view); +} +#endif + +SIMDJSON_CONSTEXPR_LAMBDA inline size_t +find_next_json_quotable_character(const std::string_view view, + size_t location) noexcept { + + for (auto pos = view.begin() + location; pos != view.end(); ++pos) { + if (json_quotable_character[static_cast(*pos)]) { + return pos - view.begin(); + } + } + return size_t(view.size()); +} + +SIMDJSON_CONSTEXPR_LAMBDA static std::string_view control_chars[] = { + "\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", "\\u0006", + "\\u0007", "\\b", "\\t", "\\n", "\\u000b", "\\f", "\\r", + "\\u000e", "\\u000f", "\\u0010", "\\u0011", "\\u0012", "\\u0013", "\\u0014", + "\\u0015", "\\u0016", "\\u0017", "\\u0018", "\\u0019", "\\u001a", "\\u001b", + "\\u001c", "\\u001d", "\\u001e", "\\u001f"}; + +// All Unicode characters may be placed within the quotation marks, except for +// the characters that MUST be escaped: quotation mark, reverse solidus, and the +// control characters (U+0000 through U+001F). There are two-character sequence +// escape representations of some popular characters: +// \", \\, \b, \f, \n, \r, \t. +SIMDJSON_CONSTEXPR_LAMBDA void escape_json_char(char c, char *&out) { + if (c == '"') { + memcpy(out, "\\\"", 2); + out += 2; + } else if (c == '\\') { + memcpy(out, "\\\\", 2); + out += 2; + } else { + std::string_view v = control_chars[uint8_t(c)]; + memcpy(out, v.data(), v.size()); + out += v.size(); + } +} + +inline size_t write_string_escaped(const std::string_view input, char *out) { + size_t mysize = input.size(); + if (!fast_needs_escaping(input)) { // fast path! + memcpy(out, input.data(), input.size()); + return input.size(); + } + const char *const initout = out; + size_t location = find_next_json_quotable_character(input, 0); + memcpy(out, input.data(), location); + out += location; + escape_json_char(input[location], out); + location += 1; + while (location < mysize) { + size_t newlocation = find_next_json_quotable_character(input, location); + memcpy(out, input.data() + location, newlocation - location); + out += newlocation - location; + location = newlocation; + if (location == mysize) { + break; + } + escape_json_char(input[location], out); + location += 1; + } + return out - initout; +} + +simdjson_inline string_builder::string_builder(size_t initial_capacity) + : buffer(new(std::nothrow) char[initial_capacity]), position(0), + capacity(buffer.get() != nullptr ? initial_capacity : 0), + is_valid(buffer.get() != nullptr) {} + +simdjson_inline bool string_builder::capacity_check(size_t upcoming_bytes) { + // We use the convention that when is_valid is false, then the capacity and + // the position are 0. + // Most of the time, this function will return true. + if (simdjson_likely(upcoming_bytes <= capacity - position)) { + return true; + } + // check for overflow, most of the time there is no overflow + if (simdjson_likely(position + upcoming_bytes < position)) { + return false; + } + // We will rarely get here. + grow_buffer((std::max)(capacity * 2, position + upcoming_bytes)); + // If the buffer allocation failed, we set is_valid to false. + return is_valid; +} + +simdjson_inline void string_builder::grow_buffer(size_t desired_capacity) { + if (!is_valid) { + return; + } + std::unique_ptr new_buffer(new (std::nothrow) char[desired_capacity]); + if (new_buffer.get() == nullptr) { + set_valid(false); + return; + } + std::memcpy(new_buffer.get(), buffer.get(), position); + buffer.swap(new_buffer); + capacity = desired_capacity; +} + +simdjson_inline void string_builder::set_valid(bool valid) noexcept { + if (!valid) { + is_valid = false; + capacity = 0; + position = 0; + buffer.reset(); + } else { + is_valid = true; + } +} + +simdjson_inline size_t string_builder::size() const noexcept { + return position; +} + +simdjson_inline void string_builder::append(char c) noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = c; + } +} + +simdjson_inline void string_builder::append_null() noexcept { + constexpr char null_literal[] = "null"; + constexpr size_t null_len = sizeof(null_literal) - 1; + if (capacity_check(null_len)) { + std::memcpy(buffer.get() + position, null_literal, null_len); + position += null_len; + } +} + +simdjson_inline void string_builder::clear() noexcept { + position = 0; + // if it was invalid, we should try to repair it + if (!is_valid) { + capacity = 0; + buffer.reset(); + is_valid = true; + } +} + +namespace internal { + +template ::value>::type> +simdjson_really_inline int int_log2(number_type x) { + return 63 - leading_zeroes(uint64_t(x) | 1); +} + +simdjson_really_inline int fast_digit_count_32(uint32_t x) { + static uint64_t table[] = { + 4294967296, 8589934582, 8589934582, 8589934582, 12884901788, + 12884901788, 12884901788, 17179868184, 17179868184, 17179868184, + 21474826480, 21474826480, 21474826480, 21474826480, 25769703776, + 25769703776, 25769703776, 30063771072, 30063771072, 30063771072, + 34349738368, 34349738368, 34349738368, 34349738368, 38554705664, + 38554705664, 38554705664, 41949672960, 41949672960, 41949672960, + 42949672960, 42949672960}; + return uint32_t((x + table[int_log2(x)]) >> 32); +} + +simdjson_really_inline int fast_digit_count_64(uint64_t x) { + static uint64_t table[] = {9, + 99, + 999, + 9999, + 99999, + 999999, + 9999999, + 99999999, + 999999999, + 9999999999, + 99999999999, + 999999999999, + 9999999999999, + 99999999999999, + 999999999999999ULL, + 9999999999999999ULL, + 99999999999999999ULL, + 999999999999999999ULL, + 9999999999999999999ULL}; + int y = (19 * int_log2(x) >> 6); + y += x > table[y]; + return y + 1; +} + +template ::value>::type> +simdjson_really_inline size_t digit_count(number_type v) noexcept { + static_assert(sizeof(number_type) == 8 || sizeof(number_type) == 4 || + sizeof(number_type) == 2 || sizeof(number_type) == 1, + "We only support 8-bit, 16-bit, 32-bit and 64-bit numbers"); + SIMDJSON_IF_CONSTEXPR(sizeof(number_type) <= 4) { + return fast_digit_count_32(static_cast(v)); + } + else { + return fast_digit_count_64(static_cast(v)); + } +} +static const char decimal_table[200] = { + 0x30, 0x30, 0x30, 0x31, 0x30, 0x32, 0x30, 0x33, 0x30, 0x34, 0x30, 0x35, + 0x30, 0x36, 0x30, 0x37, 0x30, 0x38, 0x30, 0x39, 0x31, 0x30, 0x31, 0x31, + 0x31, 0x32, 0x31, 0x33, 0x31, 0x34, 0x31, 0x35, 0x31, 0x36, 0x31, 0x37, + 0x31, 0x38, 0x31, 0x39, 0x32, 0x30, 0x32, 0x31, 0x32, 0x32, 0x32, 0x33, + 0x32, 0x34, 0x32, 0x35, 0x32, 0x36, 0x32, 0x37, 0x32, 0x38, 0x32, 0x39, + 0x33, 0x30, 0x33, 0x31, 0x33, 0x32, 0x33, 0x33, 0x33, 0x34, 0x33, 0x35, + 0x33, 0x36, 0x33, 0x37, 0x33, 0x38, 0x33, 0x39, 0x34, 0x30, 0x34, 0x31, + 0x34, 0x32, 0x34, 0x33, 0x34, 0x34, 0x34, 0x35, 0x34, 0x36, 0x34, 0x37, + 0x34, 0x38, 0x34, 0x39, 0x35, 0x30, 0x35, 0x31, 0x35, 0x32, 0x35, 0x33, + 0x35, 0x34, 0x35, 0x35, 0x35, 0x36, 0x35, 0x37, 0x35, 0x38, 0x35, 0x39, + 0x36, 0x30, 0x36, 0x31, 0x36, 0x32, 0x36, 0x33, 0x36, 0x34, 0x36, 0x35, + 0x36, 0x36, 0x36, 0x37, 0x36, 0x38, 0x36, 0x39, 0x37, 0x30, 0x37, 0x31, + 0x37, 0x32, 0x37, 0x33, 0x37, 0x34, 0x37, 0x35, 0x37, 0x36, 0x37, 0x37, + 0x37, 0x38, 0x37, 0x39, 0x38, 0x30, 0x38, 0x31, 0x38, 0x32, 0x38, 0x33, + 0x38, 0x34, 0x38, 0x35, 0x38, 0x36, 0x38, 0x37, 0x38, 0x38, 0x38, 0x39, + 0x39, 0x30, 0x39, 0x31, 0x39, 0x32, 0x39, 0x33, 0x39, 0x34, 0x39, 0x35, + 0x39, 0x36, 0x39, 0x37, 0x39, 0x38, 0x39, 0x39, +}; +} // namespace internal + +template +simdjson_inline void string_builder::append(number_type v) noexcept { + static_assert(std::is_same::value || + std::is_integral::value || + std::is_floating_point::value, + "Unsupported number type"); + // If C++17 is available, we can 'if constexpr' here. + SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + if (v) { + constexpr char true_literal[] = "true"; + constexpr size_t true_len = sizeof(true_literal) - 1; + if (capacity_check(true_len)) { + std::memcpy(buffer.get() + position, true_literal, true_len); + position += true_len; + } + } else { + constexpr char false_literal[] = "false"; + constexpr size_t false_len = sizeof(false_literal) - 1; + if (capacity_check(false_len)) { + std::memcpy(buffer.get() + position, false_literal, false_len); + position += false_len; + } + } + } + else SIMDJSON_IF_CONSTEXPR(std::is_unsigned::value) { + constexpr size_t max_number_size = 20; + if (capacity_check(max_number_size)) { + using unsigned_type = typename std::make_unsigned::type; + unsigned_type pv = static_cast(v); + size_t dc = internal::digit_count(pv); + char *write_pointer = buffer.get() + position + dc - 1; + while (pv >= 100) { + memcpy(write_pointer - 1, &internal::decimal_table[(pv % 100) * 2], 2); + write_pointer -= 2; + pv /= 100; + } + if (pv >= 10) { + *write_pointer-- = char('0' + (pv % 10)); + pv /= 10; + } + *write_pointer = char('0' + pv); + position += dc; + } + } + else SIMDJSON_IF_CONSTEXPR(std::is_integral::value) { + constexpr size_t max_number_size = 20; + if (capacity_check(max_number_size)) { + using unsigned_type = typename std::make_unsigned::type; + bool negative = v < 0; + unsigned_type pv = static_cast(v); + if (negative) { + pv = 0 - pv; // the 0 is for Microsoft + } + size_t dc = internal::digit_count(pv); + if (negative) { + buffer.get()[position++] = '-'; + } + char *write_pointer = buffer.get() + position + dc - 1; + while (pv >= 100) { + memcpy(write_pointer - 1, &internal::decimal_table[(pv % 100) * 2], 2); + write_pointer -= 2; + pv /= 100; + } + if (pv >= 10) { + *write_pointer-- = char('0' + (pv % 10)); + pv /= 10; + } + *write_pointer = char('0' + pv); + position += dc; + } + } + else SIMDJSON_IF_CONSTEXPR(std::is_floating_point::value) { + constexpr size_t max_number_size = 24; + if (capacity_check(max_number_size)) { + // We could specialize for float. + char *end = simdjson::internal::to_chars(buffer.get() + position, nullptr, + double(v)); + position = end - buffer.get(); + } + } +} + +simdjson_inline void +string_builder::escape_and_append(std::string_view input) noexcept { + // escaping might turn a control character into \x00xx so 6 characters. + if (capacity_check(6 * input.size())) { + position += write_string_escaped(input, buffer.get() + position); + } +} + +simdjson_inline void +string_builder::escape_and_append_with_quotes(std::string_view input) noexcept { + // escaping might turn a control character into \x00xx so 6 characters. + if (capacity_check(2 + 6 * input.size())) { + buffer.get()[position++] = '"'; + position += write_string_escaped(input, buffer.get() + position); + buffer.get()[position++] = '"'; + } +} + +simdjson_inline void +string_builder::escape_and_append_with_quotes(char input) noexcept { + // escaping might turn a control character into \x00xx so 6 characters. + if (capacity_check(2 + 6 * 1)) { + buffer.get()[position++] = '"'; + std::string_view cinput(&input, 1); + position += write_string_escaped(cinput, buffer.get() + position); + buffer.get()[position++] = '"'; + } +} + +simdjson_inline void +string_builder::escape_and_append_with_quotes(const char *input) noexcept { + std::string_view cinput(input); + escape_and_append_with_quotes(cinput); +} +#if SIMDJSON_SUPPORTS_CONCEPTS +template +simdjson_inline void string_builder::escape_and_append_with_quotes() noexcept { + escape_and_append_with_quotes(constevalutil::string_constant::value); +} +#endif + +simdjson_inline void string_builder::append_raw(const char *c) noexcept { + size_t len = std::strlen(c); + append_raw(c, len); +} + +simdjson_inline void +string_builder::append_raw(std::string_view input) noexcept { + if (capacity_check(input.size())) { + std::memcpy(buffer.get() + position, input.data(), input.size()); + position += input.size(); + } +} + +simdjson_inline void string_builder::append_raw(const char *str, + size_t len) noexcept { + if (capacity_check(len)) { + std::memcpy(buffer.get() + position, str, len); + position += len; + } +} +#if SIMDJSON_SUPPORTS_CONCEPTS +// Support for optional types (std::optional, etc.) +template + requires(!require_custom_serialization) +simdjson_inline void string_builder::append(const T &opt) { + if (opt) { + append(*opt); + } else { + append_null(); + } +} + +template + requires(require_custom_serialization) +simdjson_inline void string_builder::append(const T &val) { + serialize(*this, val); +} + +template + requires(std::is_convertible::value || + std::is_same::value) +simdjson_inline void string_builder::append(const T &value) { + escape_and_append_with_quotes(value); +} +#endif + +#if SIMDJSON_SUPPORTS_RANGES && SIMDJSON_SUPPORTS_CONCEPTS +// Support for range-based appending (std::ranges::view, etc.) +template + requires(!std::is_convertible::value) +simdjson_inline void string_builder::append(const R &range) noexcept { + auto it = std::ranges::begin(range); + auto end = std::ranges::end(range); + if constexpr (concepts::is_pair) { + start_object(); + + if (it == end) { + end_object(); + return; // Handle empty range + } + // Append first item without leading comma + append_key_value(it->first, it->second); + ++it; + + // Append remaining items with preceding commas + for (; it != end; ++it) { + append_comma(); + append_key_value(it->first, it->second); + } + end_object(); + } else { + start_array(); + if (it == end) { + end_array(); + return; // Handle empty range + } + + // Append first item without leading comma + append(*it); + ++it; + + // Append remaining items with preceding commas + for (; it != end; ++it) { + append_comma(); + append(*it); + } + end_array(); + } +} + +#endif + +#if SIMDJSON_EXCEPTIONS +simdjson_inline string_builder::operator std::string() const noexcept(false) { + return std::string(operator std::string_view()); +} + +simdjson_inline string_builder::operator std::string_view() const + noexcept(false) simdjson_lifetime_bound { + return view(); +} +#endif + +simdjson_inline simdjson_result +string_builder::view() const noexcept { + if (!is_valid) { + return simdjson::OUT_OF_CAPACITY; + } + return std::string_view(buffer.get(), position); +} + +simdjson_inline simdjson_result string_builder::c_str() noexcept { + if (capacity_check(1)) { + buffer.get()[position] = '\0'; + return buffer.get(); + } + return simdjson::OUT_OF_CAPACITY; +} + +simdjson_inline bool string_builder::validate_unicode() const noexcept { + return simdjson::validate_utf8(buffer.get(), position); +} + +simdjson_inline void string_builder::start_object() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = '{'; + } +} + +simdjson_inline void string_builder::end_object() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = '}'; + } +} + +simdjson_inline void string_builder::start_array() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = '['; + } +} + +simdjson_inline void string_builder::end_array() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = ']'; + } +} + +simdjson_inline void string_builder::append_comma() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = ','; + } +} + +simdjson_inline void string_builder::append_colon() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = ':'; + } +} + +template +simdjson_inline void +string_builder::append_key_value(key_type key, value_type value) noexcept { + static_assert(std::is_same::value || + std::is_convertible::value, + "Unsupported key type"); + escape_and_append_with_quotes(key); + append_colon(); + SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + append_null(); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR( + std::is_convertible::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else { + append(value); + } +} + +#if SIMDJSON_SUPPORTS_CONCEPTS +template +simdjson_inline void +string_builder::append_key_value(value_type value) noexcept { + escape_and_append_with_quotes(); + append_colon(); + SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + append_null(); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR( + std::is_convertible::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else { + append(value); + } +} +#endif + +} // namespace builder +} // namespace fallback +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_STRING_BUILDER_INL_H +/* end file simdjson/generic/ondemand/json_string_builder-inl.h for fallback */ +/* including simdjson/generic/ondemand/json_builder.h for fallback: #include "simdjson/generic/ondemand/json_builder.h" */ +/* begin file simdjson/generic/ondemand/json_builder.h for fallback */ +/** + * This file is part of the builder API. It is temporarily in the ondemand directory + * but we will move it to a builder directory later. + */ +#ifndef SIMDJSON_GENERIC_BUILDER_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_string_builder.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/concepts.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ +#if SIMDJSON_STATIC_REFLECTION + +#include +#include +#include +#include +#include +#include +#include +#include +// #include // for std::define_static_string - header not available yet + +namespace simdjson { +namespace fallback { +namespace builder { + +template + requires(concepts::container_but_not_string && !require_custom_serialization) +constexpr void atom(string_builder &b, const T &t) { + auto it = t.begin(); + auto end = t.end(); + if (it == end) { + b.append_raw("[]"); + return; + } + b.append('['); + atom(b, *it); + ++it; + for (; it != end; ++it) { + b.append(','); + atom(b, *it); + } + b.append(']'); +} + +template + requires(std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v) +constexpr void atom(string_builder &b, const T &t) { + b.escape_and_append_with_quotes(t); +} + +template + requires(!require_custom_serialization) +constexpr void atom(string_builder &b, const T &m) { + if (m.empty()) { + b.append_raw("{}"); + return; + } + b.append('{'); + bool first = true; + for (const auto& [key, value] : m) { + if (!first) { + b.append(','); + } + first = false; + // Keys must be convertible to string_view per the concept + b.escape_and_append_with_quotes(key); + b.append(':'); + atom(b, value); + } + b.append('}'); +} + + +template::value && !std::is_same_v>::type> +constexpr void atom(string_builder &b, const number_type t) { + b.append(t); +} + +template + requires(std::is_class_v && !concepts::container_but_not_string && + !concepts::string_view_keyed_map && + !concepts::optional_type && + !concepts::smart_pointer && + !concepts::appendable_containers && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && !require_custom_serialization) +constexpr void atom(string_builder &b, const T &t) { + int i = 0; + b.append('{'); + template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) { + if (i != 0) + b.append(','); + constexpr auto key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(dm))); + b.append_raw(key); + b.append(':'); + atom(b, t.[:dm:]); + i++; + }; + b.append('}'); +} + +// Support for optional types (std::optional, etc.) +template + requires(!require_custom_serialization) +constexpr void atom(string_builder &b, const T &opt) { + if (opt) { + atom(b, opt.value()); + } else { + b.append_raw("null"); + } +} + +// Support for smart pointers (std::unique_ptr, std::shared_ptr, etc.) +template + requires(!require_custom_serialization) +constexpr void atom(string_builder &b, const T &ptr) { + if (ptr) { + atom(b, *ptr); + } else { + b.append_raw("null"); + } +} + +// Support for enums - serialize as string representation using expand approach from P2996R12 +template + requires(std::is_enum_v && !require_custom_serialization) +void atom(string_builder &b, const T &e) { +#if SIMDJSON_STATIC_REFLECTION + constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T)); + template for (constexpr auto enum_val : enumerators) { + constexpr auto enum_str = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(enum_val))); + if (e == [:enum_val:]) { + b.append_raw(enum_str); + return; + } + }; + // Fallback to integer if enum value not found + atom(b, static_cast>(e)); +#else + // Fallback: serialize as integer if reflection not available + atom(b, static_cast>(e)); +#endif +} + +// Support for appendable containers that don't have operator[] (sets, etc.) +template + requires(!concepts::container_but_not_string && !concepts::string_view_keyed_map && + !concepts::optional_type && !concepts::smart_pointer && + !std::is_same_v && + !std::is_same_v && !std::is_same_v && !require_custom_serialization) +constexpr void atom(string_builder &b, const T &container) { + if (container.empty()) { + b.append_raw("[]"); + return; + } + b.append('['); + bool first = true; + for (const auto& item : container) { + if (!first) { + b.append(','); + } + first = false; + atom(b, item); + } + b.append(']'); +} + +// append functions that delegate to atom functions for primitive types +template + requires(std::is_arithmetic_v && !std::is_same_v) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!concepts::container_but_not_string && !concepts::string_view_keyed_map && + !concepts::optional_type && !concepts::smart_pointer && + !std::is_same_v && + !std::is_same_v && !std::is_same_v && !require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +// works for struct +template + requires(std::is_class_v && !concepts::container_but_not_string && + !concepts::string_view_keyed_map && + !concepts::optional_type && + !concepts::smart_pointer && + !concepts::appendable_containers && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && !require_custom_serialization) +void append(string_builder &b, const Z &z) { + int i = 0; + b.append('{'); + template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^Z, std::meta::access_context::unchecked()))) { + if (i != 0) + b.append(','); + constexpr auto key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(dm))); + b.append_raw(key); + b.append(':'); + atom(b, z.[:dm:]); + i++; + }; + b.append('}'); +} + +// works for container that have begin() and end() iterators +template + requires(concepts::container_but_not_string && !require_custom_serialization) +void append(string_builder &b, const Z &z) { + auto it = z.begin(); + auto end = z.end(); + if (it == end) { + b.append_raw("[]"); + return; + } + b.append('['); + atom(b, *it); + ++it; + for (; it != end; ++it) { + b.append(','); + atom(b, *it); + } + b.append(']'); +} + +template + requires (require_custom_serialization) +void append(string_builder &b, const Z &z) { + b.append(z); +} + + +template +simdjson_warn_unused simdjson_result to_json_string(const Z &z, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) { + string_builder b(initial_capacity); + append(b, z); + std::string_view s; + if(auto e = b.view().get(s); e) { return e; } + return std::string(s); +} + +template +simdjson_warn_unused simdjson_error to_json(const Z &z, std::string &s, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) { + string_builder b(initial_capacity); + append(b, z); + std::string_view view; + if(auto e = b.view().get(view); e) { return e; } + s.assign(view); + return SUCCESS; +} + +template +string_builder& operator<<(string_builder& b, const Z& z) { + append(b, z); + return b; +} + +// extract_from: Serialize only specific fields from a struct to JSON +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +void extract_from(string_builder &b, const T &obj) { + // Helper to check if a field name matches any of the requested fields + auto should_extract = [](std::string_view field_name) constexpr -> bool { + return ((FieldNames.view() == field_name) || ...); + }; + + b.append('{'); + bool first = true; + + // Iterate through all members of T using reflection + template for (constexpr auto mem : std::define_static_array( + std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) { + + if constexpr (std::meta::is_public(mem)) { + constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem)); + + // Only serialize this field if it's in our list of requested fields + if constexpr (should_extract(key)) { + if (!first) { + b.append(','); + } + first = false; + + // Serialize the key + constexpr auto quoted_key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(mem))); + b.append_raw(quoted_key); + b.append(':'); + + // Serialize the value + atom(b, obj.[:mem:]); + } + } + }; + + b.append('}'); +} + +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_result extract_from(const T &obj, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) { + string_builder b(initial_capacity); + extract_from(b, obj); + std::string_view s; + if(auto e = b.view().get(s); e) { return e; } + return std::string(s); +} + +} // namespace builder +} // namespace fallback +// Alias the function template to 'to' in the global namespace +template +simdjson_warn_unused simdjson_result to_json(const Z &z, size_t initial_capacity = fallback::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { + fallback::builder::string_builder b(initial_capacity); + fallback::builder::append(b, z); + std::string_view s; + if(auto e = b.view().get(s); e) { return e; } + return std::string(s); +} +template +simdjson_warn_unused simdjson_error to_json(const Z &z, std::string &s, size_t initial_capacity = fallback::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { + fallback::builder::string_builder b(initial_capacity); + fallback::builder::append(b, z); + std::string_view view; + if(auto e = b.view().get(view); e) { return e; } + s.assign(view); + return SUCCESS; +} +// Global namespace function for extract_from +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_result extract_from(const T &obj, size_t initial_capacity = fallback::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { + fallback::builder::string_builder b(initial_capacity); + fallback::builder::extract_from(b, obj); + std::string_view s; + if(auto e = b.view().get(s); e) { return e; } + return std::string(s); +} + +} // namespace simdjson + +#endif // SIMDJSON_STATIC_REFLECTION + +#endif +/* end file simdjson/generic/ondemand/json_builder.h for fallback */ /* end file simdjson/generic/ondemand/amalgamated.h for fallback */ /* including simdjson/fallback/end.h: #include "simdjson/fallback/end.h" */ @@ -54572,7 +60417,7 @@ using namespace simd; struct backslash_and_quote { public: static constexpr uint32_t BYTES_PROCESSED = 32; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); + simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; } simdjson_inline bool has_backslash() { return ((quote_bits - 1) & bs_bits) != 0; } @@ -54596,6 +60441,31 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin }; } + +struct escaping { + static constexpr uint32_t BYTES_PROCESSED = 32; + simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst); + + simdjson_inline bool has_escape() { return escape_bits != 0; } + simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); } + + uint64_t escape_bits; +}; // struct escaping + + + +simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) { + static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes"); + simd8 v(src); + v.store(dst); + simd8 is_quote = (v == '"'); + simd8 is_backslash = (v == '\\'); + simd8 is_control = (v < 32); + return { + uint64_t((is_backslash | is_quote | is_control).to_bitmask()) + }; +} + } // unnamed namespace } // namespace haswell } // namespace simdjson @@ -54662,7 +60532,7 @@ class value_iterator; /* end file simdjson/generic/ondemand/base.h for haswell */ /* including simdjson/generic/ondemand/deserialize.h for haswell: #include "simdjson/generic/ondemand/deserialize.h" */ /* begin file simdjson/generic/ondemand/deserialize.h for haswell */ -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS #ifndef SIMDJSON_ONDEMAND_DESERIALIZE_H /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ @@ -54671,55 +60541,8 @@ class value_iterator; /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ -#include namespace simdjson { -namespace tag_invoke_fn_ns { -void tag_invoke(); - -struct tag_invoke_fn { - template - requires requires(Tag tag, Args &&...args) { - tag_invoke(std::forward(tag), std::forward(args)...); - } - constexpr auto operator()(Tag tag, Args &&...args) const - noexcept(noexcept(tag_invoke(std::forward(tag), - std::forward(args)...))) - -> decltype(tag_invoke(std::forward(tag), - std::forward(args)...)) { - return tag_invoke(std::forward(tag), std::forward(args)...); - } -}; -} // namespace tag_invoke_fn_ns - -inline namespace tag_invoke_ns { -inline constexpr tag_invoke_fn_ns::tag_invoke_fn tag_invoke = {}; -} // namespace tag_invoke_ns - -template -concept tag_invocable = requires(Tag tag, Args... args) { - tag_invoke(std::forward(tag), std::forward(args)...); -}; - -template -concept nothrow_tag_invocable = - tag_invocable && requires(Tag tag, Args... args) { - { - tag_invoke(std::forward(tag), std::forward(args)...) - } noexcept; - }; - -template -using tag_invoke_result = - std::invoke_result; - -template -using tag_invoke_result_t = - std::invoke_result_t; - -template using tag_t = std::decay_t; - - struct deserialize_tag; /// These types are deserializable in a built-in way @@ -54741,7 +60564,7 @@ template concept custom_deserializable = tag_invocable; template -concept deserializable = custom_deserializable || is_builtin_deserializable_v; +concept deserializable = custom_deserializable || is_builtin_deserializable_v || concepts::optional_type; template concept nothrow_custom_deserializable = nothrow_tag_invocable; @@ -54752,28 +60575,44 @@ concept nothrow_deserializable = nothrow_custom_deserializable || is_bu /// Deserialize Tag inline constexpr struct deserialize_tag { + using array_type = haswell::ondemand::array; + using object_type = haswell::ondemand::object; using value_type = haswell::ondemand::value; using document_type = haswell::ondemand::document; using document_reference_type = haswell::ondemand::document_reference; + // Customization Point for array + template + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(array_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for object + template + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(object_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + // Customization Point for value template requires custom_deserializable - [[nodiscard]] constexpr /* error_code */ auto operator()(value_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + simdjson_warn_unused constexpr /* error_code */ auto operator()(value_type &object, T& output) const noexcept(nothrow_custom_deserializable) { return tag_invoke(*this, object, output); } // Customization Point for document template requires custom_deserializable - [[nodiscard]] constexpr /* error_code */ auto operator()(document_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + simdjson_warn_unused constexpr /* error_code */ auto operator()(document_type &object, T& output) const noexcept(nothrow_custom_deserializable) { return tag_invoke(*this, object, output); } // Customization Point for document reference template requires custom_deserializable - [[nodiscard]] constexpr /* error_code */ auto operator()(document_reference_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + simdjson_warn_unused constexpr /* error_code */ auto operator()(document_reference_type &object, T& output) const noexcept(nothrow_custom_deserializable) { return tag_invoke(*this, object, output); } @@ -54783,7 +60622,7 @@ inline constexpr struct deserialize_tag { } // namespace simdjson #endif // SIMDJSON_ONDEMAND_DESERIALIZE_H -#endif // SIMDJSON_SUPPORTS_DESERIALIZATION +#endif // SIMDJSON_SUPPORTS_CONCEPTS /* end file simdjson/generic/ondemand/deserialize.h for haswell */ /* including simdjson/generic/ondemand/value_iterator.h for haswell: #include "simdjson/generic/ondemand/value_iterator.h" */ @@ -55125,7 +60964,7 @@ class value_iterator { simdjson_warn_unused simdjson_inline simdjson_result get_root_number(bool check_trailing) noexcept; simdjson_warn_unused simdjson_inline simdjson_result is_root_null(bool check_trailing) noexcept; - simdjson_inline error_code error() const noexcept; + simdjson_warn_unused simdjson_inline error_code error() const noexcept; simdjson_inline uint8_t *&string_buf_loc() noexcept; simdjson_inline const json_iterator &json_iter() const noexcept; simdjson_inline json_iterator &json_iter() noexcept; @@ -55209,8 +61048,8 @@ class value_iterator { simdjson_inline const uint8_t *peek_non_root_scalar(const char *type) noexcept; - simdjson_inline error_code start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept; - simdjson_inline error_code end_container() noexcept; + simdjson_warn_unused simdjson_inline error_code start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept; + simdjson_warn_unused simdjson_inline error_code end_container() noexcept; /** * Advance to a place expecting a value (increasing depth). @@ -55220,8 +61059,8 @@ class value_iterator { */ simdjson_inline simdjson_result advance_to_value() noexcept; - simdjson_inline error_code incorrect_type_error(const char *message) const noexcept; - simdjson_inline error_code error_unless_more_tokens(uint32_t tokens=1) const noexcept; + simdjson_warn_unused simdjson_inline error_code incorrect_type_error(const char *message) const noexcept; + simdjson_warn_unused simdjson_inline error_code error_unless_more_tokens(uint32_t tokens=1) const noexcept; simdjson_inline bool is_at_start() const noexcept; /** @@ -55258,7 +61097,7 @@ class value_iterator { /** @copydoc error_code json_iterator::end_position() const noexcept; */ simdjson_inline token_position end_position() const noexcept; /** @copydoc error_code json_iterator::report_error(error_code error, const char *message) noexcept; */ - simdjson_inline error_code report_error(error_code error, const char *message) noexcept; + simdjson_warn_unused simdjson_inline error_code report_error(error_code error, const char *message) noexcept; friend class document; friend class object; @@ -55323,13 +61162,14 @@ class value { * * You may use get_double(), get_bool(), get_uint64(), get_int64(), * get_object(), get_array(), get_raw_json_string(), or get_string() instead. + * When SIMDJSON_SUPPORTS_CONCEPTS is set, custom types are also supported. * * @returns A value of the given type, parsed from the JSON. * @returns INCORRECT_TYPE If the JSON value is not the given type. */ template simdjson_inline simdjson_result get() -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept @@ -55346,22 +61186,38 @@ class value { * Get this value as the given type. * * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool + * If the macro SIMDJSON_SUPPORTS_CONCEPTS is set, then custom types are also supported. * * @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized. * @returns INCORRECT_TYPE If the JSON value is not an object. * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. */ template - simdjson_inline error_code get(T &out) -#if SIMDJSON_SUPPORTS_DESERIALIZATION + simdjson_warn_unused simdjson_inline error_code get(T &out) +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept #endif { - #if SIMDJSON_SUPPORTS_DESERIALIZATION + #if SIMDJSON_SUPPORTS_CONCEPTS if constexpr (custom_deserializable) { return deserialize(*this, out); + } else if constexpr (concepts::optional_type) { + using value_type = typename std::remove_cvref_t::value_type; + + // Check if the value is null + bool is_null_value; + SIMDJSON_TRY( is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); // Set to nullopt + return SUCCESS; + } + + if (!out) { + out.emplace(); + } + return get(out.value()); } else { static_assert(!sizeof(T), "The get method with type T is not implemented by the simdjson library. " "And you do not seem to have added support for it. Indeed, we have that " @@ -55371,7 +61227,7 @@ class value { static_cast(out); // to get rid of unused errors return UNINITIALIZED; } -#else // SIMDJSON_SUPPORTS_DESERIALIZATION +#else // SIMDJSON_SUPPORTS_CONCEPTS // Unless the simdjson library or the user provides an inline implementation, calling this method should // immediately fail. static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " @@ -55489,7 +61345,7 @@ class value { * @returns INCORRECT_TYPE if the JSON value is not a string. Otherwise, we return SUCCESS. */ template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; /** * Cast this JSON value to a "wobbly" string. @@ -56010,7 +61866,7 @@ struct simdjson_result : public haswell::implementatio simdjson_inline simdjson_result get_double_in_string() noexcept; simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; simdjson_inline simdjson_result get_wobbly_string() noexcept; simdjson_inline simdjson_result get_raw_json_string() noexcept; simdjson_inline simdjson_result get_bool() noexcept; @@ -56091,7 +61947,22 @@ struct simdjson_result : public haswell::implementatio simdjson_result operator[](int) noexcept = delete; /** - * Get the type of this JSON value. + * Get the type of this JSON value. It does not validate or consume the value. + * E.g., you must still call "is_null()" to check that a value is null even if + * "type()" returns json_type::null. + * + * Given a valid JSON document, the answer can be one of + * simdjson::ondemand::json_type::object, + * simdjson::ondemand::json_type::array, + * simdjson::ondemand::json_type::string, + * simdjson::ondemand::json_type::number, + * simdjson::ondemand::json_type::boolean, + * simdjson::ondemand::json_type::null. + * + * Starting with simdjson 4.0, this function will return simdjson::ondemand::json_type::unknown + * given a bad token. + * This allows you to identify a case such as {"key": NaN} and identify the NaN value. + * The simdjson::ondemand::json_type::unknown value should only happen with non-valid JSON. * * NOTE: If you're only expecting a value to be one type (a typical case), it's generally * better to just call .get_double, .get_string, etc. and check for INCORRECT_TYPE (or just @@ -56585,14 +62456,14 @@ class json_iterator { * @param error The error to report. Must not be SUCCESS, UNINITIALIZED, INCORRECT_TYPE, or NO_SUCH_FIELD. * @param message An error message to report with the error. */ - simdjson_inline error_code report_error(error_code error, const char *message) noexcept; + simdjson_warn_unused simdjson_inline error_code report_error(error_code error, const char *message) noexcept; /** * Log error, but don't stop iteration. * @param error The error to report. Must be INCORRECT_TYPE, or NO_SUCH_FIELD. * @param message An error message to report with the error. */ - simdjson_inline error_code optional_error(error_code error, const char *message) noexcept; + simdjson_warn_unused simdjson_inline error_code optional_error(error_code error, const char *message) noexcept; /** * Take an input in json containing max_len characters and attempt to copy it over to tmpbuf, a buffer with @@ -56612,7 +62483,7 @@ class json_iterator { simdjson_inline void reenter_child(token_position position, depth_t child_depth) noexcept; - simdjson_inline error_code consume_character(char c) noexcept; + simdjson_warn_unused simdjson_inline error_code consume_character(char c) noexcept; #if SIMDJSON_DEVELOPMENT_CHECKS simdjson_inline token_position start_position(depth_t depth) const noexcept; simdjson_inline void set_start_position(depth_t depth, token_position position) noexcept; @@ -56703,6 +62574,7 @@ namespace ondemand { * The type of a JSON value. */ enum class json_type { + unknown=0, // Start at 1 to catch uninitialized / default values more easily array=1, ///< A JSON array ( [ 1, 2, 3 ... ] ) object, ///< A JSON object ( { "a": 1, "b" 2, ... } ) @@ -56909,6 +62781,12 @@ class raw_json_string { */ simdjson_inline const char * raw() const noexcept; + /** + * Get the character at index i. This is unchecked. + * [0] when the string is of length 0 returns the final quote ("). + */ + simdjson_inline char operator[](size_t i) const noexcept; + /** * This compares the current instance to the std::string_view target: returns true if * they are byte-by-byte equal (no escaping is done) on target.size() characters, @@ -57048,10 +62926,10 @@ struct simdjson_result : public haswell::imp simdjson_inline ~simdjson_result() noexcept = default; ///< @private simdjson_inline simdjson_result raw() const noexcept; + simdjson_inline char operator[](size_t) const noexcept; simdjson_inline simdjson_warn_unused simdjson_result unescape(haswell::ondemand::json_iterator &iter, bool allow_replacement) const noexcept; simdjson_inline simdjson_warn_unused simdjson_result unescape_wobbly(haswell::ondemand::json_iterator &iter) const noexcept; }; - } // namespace simdjson #endif // SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_H @@ -57067,6 +62945,7 @@ struct simdjson_result : public haswell::imp /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ #include +#include namespace simdjson { namespace haswell { @@ -57185,7 +63064,9 @@ class parser { simdjson_warn_unused simdjson_result iterate(std::string_view json, size_t capacity) & noexcept; /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ simdjson_warn_unused simdjson_result iterate(const std::string &json) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + /** @overload simdjson_result iterate(padded_string_view json) & noexcept + The string instance might be have its capacity extended. Note that this can still + result in AddressSanitizer: container-overflow in some cases. */ simdjson_warn_unused simdjson_result iterate(std::string &json) & noexcept; /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ simdjson_warn_unused simdjson_result iterate(const simdjson_result &json) & noexcept; @@ -57273,6 +63154,11 @@ class parser { * Setting batch_size to excessively large or excessively small values may impact negatively the * performance. * + * ### Threads + * + * When compiled with SIMDJSON_THREADS_ENABLED, this method will use a single thread under the + * hood to do some lookahead. + * * ### REQUIRED: Buffer Padding * * The buffer must have at least SIMDJSON_PADDING extra allocated bytes. It does not matter what @@ -57280,10 +63166,10 @@ class parser { * using a sanitizer that verifies that no uninitialized byte is read, then you should initialize the * SIMDJSON_PADDING bytes to avoid runtime warnings. * - * ### Threads + * This is checked automatically with all iterate_many function calls, except for the two + * that take pointers (const char* or const uint8_t*). * - * When compiled with SIMDJSON_THREADS_ENABLED, this method will use a single thread under the - * hood to do some lookahead. + * ### Threads * * ### Parser Capacity * @@ -57309,14 +63195,16 @@ class parser { */ inline simdjson_result iterate_many(const uint8_t *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ + inline simdjson_result iterate_many(padded_string_view json, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ inline simdjson_result iterate_many(const char *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ inline simdjson_result iterate_many(const std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; - inline simdjson_result iterate_many(const std::string &&s, size_t batch_size, bool allow_comma_separated = false) = delete;// unsafe + /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) + the string might be automatically padded with up to SIMDJSON_PADDING whitespace characters */ + inline simdjson_result iterate_many(std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ inline simdjson_result iterate_many(const padded_string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; - inline simdjson_result iterate_many(const padded_string &&s, size_t batch_size, bool allow_comma_separated = false) = delete;// unsafe - /** @private We do not want to allow implicit conversion from C string to std::string. */ simdjson_result iterate_many(const char *buf, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept = delete; @@ -57418,13 +63306,39 @@ class parser { bool string_buffer_overflow(const uint8_t *string_buf_loc) const noexcept; #endif + /** + * Get a unique parser instance corresponding to the current thread. + * This instance can be safely used within the current thread, but it should + * not be passed to other threads. + * + * A parser should only be used for one document at a time. + * + * Our simdjson::from functions use this parser instance. + * + * You can free the related parser by calling release_parser(). + */ + static simdjson_inline simdjson_warn_unused ondemand::parser& get_parser(); + /** + * Release the parser instance initialized by get_parser() and all the + * associated resources (memory). Returns true if a parser instance + * was released. + */ + static simdjson_inline bool release_parser(); + private: + friend bool release_parser(); + friend ondemand::parser& get_parser(); + /** Get the thread-local parser instance, allocates it if needed */ + static simdjson_inline simdjson_warn_unused std::unique_ptr& get_parser_instance(); + /** Get the thread-local parser instance, it might be null */ + static simdjson_inline simdjson_warn_unused std::unique_ptr& get_threadlocal_parser_if_exists(); /** @private [for benchmarking access] The implementation to use */ std::unique_ptr implementation{}; size_t _capacity{0}; size_t _max_capacity; size_t _max_depth{DEFAULT_MAX_DEPTH}; std::unique_ptr string_buf{}; + #if SIMDJSON_DEVELOPMENT_CHECKS std::unique_ptr start_positions{}; #endif @@ -57452,6 +63366,315 @@ struct simdjson_result : public haswell::implementati #endif // SIMDJSON_GENERIC_ONDEMAND_PARSER_H /* end file simdjson/generic/ondemand/parser.h for haswell */ +// JSON builder - needed for extract_into functionality +/* including simdjson/generic/ondemand/json_string_builder.h for haswell: #include "simdjson/generic/ondemand/json_string_builder.h" */ +/* begin file simdjson/generic/ondemand/json_string_builder.h for haswell */ +/** + * This file is part of the builder API. It is temporarily in the ondemand directory + * but we will move it to a builder directory later. + */ +#ifndef SIMDJSON_GENERIC_STRING_BUILDER_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { + + +#if SIMDJSON_SUPPORTS_CONCEPTS + +namespace haswell { +namespace builder { + class string_builder; +}} + +template +struct has_custom_serialization : std::false_type {}; + +inline constexpr struct serialize_tag { + template + requires custom_deserializable + constexpr void operator()(haswell::builder::string_builder& b, T& obj) const{ + return tag_invoke(*this, b, obj); + } + + +} serialize{}; +template +struct has_custom_serialization(), std::declval())) +>> : std::true_type {}; + +template +constexpr bool require_custom_serialization = has_custom_serialization::value; +#else +struct has_custom_serialization : std::false_type {}; +#endif // SIMDJSON_SUPPORTS_CONCEPTS + +namespace haswell { +namespace builder { +/** + * A builder for JSON strings representing documents. This is a low-level + * builder that is not meant to be used directly by end-users. Though it + * supports atomic types (Booleans, strings), it does not support composed + * types (arrays and objects). + * + * Ultimately, this class can support kernel-specific optimizations. E.g., + * it may make use of SIMD instructions to escape strings faster. + */ +class string_builder { +public: + simdjson_inline string_builder(size_t initial_capacity = DEFAULT_INITIAL_CAPACITY); + + static constexpr size_t DEFAULT_INITIAL_CAPACITY = 1024; + + /** + * Append number (includes Booleans). Booleans are mapped to the strings + * false and true. Numbers are converted to strings abiding by the JSON standard. + * Floating-point numbers are converted to the shortest string that 'correctly' + * represents the number. + */ + template::value>::type> + simdjson_inline void append(number_type v) noexcept; + + /** + * Append character c. + */ + simdjson_inline void append(char c) noexcept; + + /** + * Append the string 'null'. + */ + simdjson_inline void append_null() noexcept; + + /** + * Clear the content. + */ + simdjson_inline void clear() noexcept; + + /** + * Append the std::string_view, after escaping it. + * There is no UTF-8 validation. + */ + simdjson_inline void escape_and_append(std::string_view input) noexcept; + + /** + * Append the std::string_view surrounded by double quotes, after escaping it. + * There is no UTF-8 validation. + */ + simdjson_inline void escape_and_append_with_quotes(std::string_view input) noexcept; +#if SIMDJSON_SUPPORTS_CONCEPTS + template + simdjson_inline void escape_and_append_with_quotes() noexcept; +#endif + /** + * Append the character surrounded by double quotes, after escaping it. + * There is no UTF-8 validation. + */ + simdjson_inline void escape_and_append_with_quotes(char input) noexcept; + + /** + * Append the character surrounded by double quotes, after escaping it. + * There is no UTF-8 validation. + */ + simdjson_inline void escape_and_append_with_quotes(const char* input) noexcept; + + /** + * Append the C string directly, without escaping. + * There is no UTF-8 validation. + */ + simdjson_inline void append_raw(const char *c) noexcept; + + /** + * Append "{" to the buffer. + */ + simdjson_inline void start_object() noexcept; + + /** + * Append "}" to the buffer. + */ + simdjson_inline void end_object() noexcept; + + /** + * Append "[" to the buffer. + */ + simdjson_inline void start_array() noexcept; + + /** + * Append "]" to the buffer. + */ + simdjson_inline void end_array() noexcept; + + /** + * Append "," to the buffer. + */ + simdjson_inline void append_comma() noexcept; + + /** + * Append ":" to the buffer. + */ + simdjson_inline void append_colon() noexcept; + + /** + * Append a key-value pair to the buffer. + * The key is escaped and surrounded by double quotes. + * The value is escaped if it is a string. + */ + template + simdjson_inline void append_key_value(key_type key, value_type value) noexcept; +#if SIMDJSON_SUPPORTS_CONCEPTS + template + simdjson_inline void append_key_value(value_type value) noexcept; + + // Support for optional types (std::optional, etc.) + template + requires(!require_custom_serialization) + simdjson_inline void append(const T &opt); + + template + requires(require_custom_serialization) + simdjson_inline void append(const T &val); + + // Support for string-like types + template + requires(std::is_convertible::value || + std::is_same::value ) + simdjson_inline void append(const T &value); +#endif +#if SIMDJSON_SUPPORTS_RANGES && SIMDJSON_SUPPORTS_CONCEPTS + // Support for range-based appending (std::ranges::view, etc.) + template +requires (!std::is_convertible::value) + simdjson_inline void append(const R &range) noexcept; +#endif + /** + * Append the std::string_view directly, without escaping. + * There is no UTF-8 validation. + */ + simdjson_inline void append_raw(std::string_view input) noexcept; + + /** + * Append len characters from str. + * There is no UTF-8 validation. + */ + simdjson_inline void append_raw(const char *str, size_t len) noexcept; +#if SIMDJSON_EXCEPTIONS + /** + * Creates an std::string from the written JSON buffer. + * Throws if memory allocation failed + * + * The result may not be valid UTF-8 if some of your content was not valid UTF-8. + * Use validate_unicode() to check the content if needed. + */ + simdjson_inline operator std::string() const noexcept(false); + + /** + * Creates an std::string_view from the written JSON buffer. + * Throws if memory allocation failed. + * + * The result may not be valid UTF-8 if some of your content was not valid UTF-8. + * Use validate_unicode() to check the content if needed. + */ + simdjson_inline operator std::string_view() const noexcept(false) simdjson_lifetime_bound; +#endif + + /** + * Returns a view on the written JSON buffer. Returns an error + * if memory allocation failed. + * + * The result may not be valid UTF-8 if some of your content was not valid UTF-8. + * Use validate_unicode() to check the content. + */ + simdjson_inline simdjson_result view() const noexcept; + + /** + * Appends the null character to the buffer and returns + * a pointer to the beginning of the written JSON buffer. + * Returns an error if memory allocation failed. + * The result is null-terminated. + * + * The result may not be valid UTF-8 if some of your content was not valid UTF-8. + * Use validate_unicode() to check the content. + */ + simdjson_inline simdjson_result c_str() noexcept; + + /** + * Return true if the content is valid UTF-8. + */ + simdjson_inline bool validate_unicode() const noexcept; + + /** + * Returns the current size of the written JSON buffer. + * If an error occurred, returns 0. + */ + simdjson_inline size_t size() const noexcept; + +private: + /** + * Returns true if we can write at least upcoming_bytes bytes. + * The underlying buffer is reallocated if needed. It is designed + * to be called before writing to the buffer. It should be fast. + */ + simdjson_inline bool capacity_check(size_t upcoming_bytes); + + /** + * Grow the buffer to at least desired_capacity bytes. + * If the allocation fails, is_valid is set to false. We expect + * that this function would not be repeatedly called. + */ + simdjson_inline void grow_buffer(size_t desired_capacity); + + /** + * We use this helper function to make sure that is_valid is kept consistent. + */ + simdjson_inline void set_valid(bool valid) noexcept; + + std::unique_ptr buffer{}; + size_t position{0}; + size_t capacity{0}; + bool is_valid{true}; +}; + + + +} +} + + +#if !SIMDJSON_STATIC_REFLECTION +// fallback implementation until we have static reflection +template +simdjson_warn_unused simdjson_result to_json(const Z &z, size_t initial_capacity = simdjson::haswell::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { + simdjson::haswell::builder::string_builder b(initial_capacity); + b.append(z); + std::string_view s; + auto e = b.view().get(s); + if(e) { return e; } + return std::string(s); +} +template +simdjson_warn_unused simdjson_error to_json(const Z &z, std::string &s, size_t initial_capacity = simdjson::haswell::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { + simdjson::haswell::builder::string_builder b(initial_capacity); + b.append(z); + std::string_view sv; + auto e = b.view().get(sv); + if(e) { return e; } + s.assign(sv.data(), sv.size()); + return simdjson::SUCCESS; +} +#endif + +#if SIMDJSON_SUPPORTS_CONCEPTS +#endif // SIMDJSON_SUPPORTS_CONCEPTS + +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_STRING_BUILDER_H +/* end file simdjson/generic/ondemand/json_string_builder.h for haswell */ + // All other declarations /* including simdjson/generic/ondemand/array.h for haswell: #include "simdjson/generic/ondemand/array.h" */ /* begin file simdjson/generic/ondemand/array.h for haswell */ @@ -57588,11 +63811,42 @@ class array { * - INDEX_OUT_OF_BOUNDS if the array index is larger than an array length */ simdjson_inline simdjson_result at(size_t index) noexcept; + +#if SIMDJSON_SUPPORTS_CONCEPTS + /** + * Get this array as the given type. + * + * @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized. + * @returns INCORRECT_TYPE If the JSON array is not of the given type. + * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. + */ + template + simdjson_warn_unused simdjson_inline error_code get(T &out) + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) { + static_assert(custom_deserializable); + return deserialize(*this, out); + } + /** + * Get this array as the given type. + * + * @returns A value of the given type, parsed from the JSON. + * @returns INCORRECT_TYPE If the JSON value is not the given type. + */ + template + simdjson_inline simdjson_result get() + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) + { + static_assert(std::is_default_constructible::value, "The specified type is not default constructible."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } +#endif // SIMDJSON_SUPPORTS_CONCEPTS protected: /** * Go to the end of the array, no matter where you are right now. */ - simdjson_inline error_code consume() noexcept; + simdjson_warn_unused simdjson_inline error_code consume() noexcept; /** * Begin array iteration. @@ -57666,7 +63920,28 @@ struct simdjson_result : public haswell::implementatio simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; simdjson_inline simdjson_result raw_json() noexcept; +#if SIMDJSON_SUPPORTS_CONCEPTS + // TODO: move this code into object-inl.h + template + simdjson_inline simdjson_result get() noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + return first; + } + return first.get(); + } + template + simdjson_warn_unused simdjson_inline error_code get(T& out) noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + out = first; + } else { + SIMDJSON_TRY( first.get(out) ); + } + return SUCCESS; + } +#endif // SIMDJSON_SUPPORTS_CONCEPTS }; } // namespace simdjson @@ -57711,7 +63986,8 @@ class array_iterator { * * Part of the std::iterator interface. */ - simdjson_inline simdjson_result operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. + simdjson_inline simdjson_result + operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. /** * Check if we are at the end of the JSON. * @@ -57735,6 +64011,11 @@ class array_iterator { */ simdjson_inline array_iterator &operator++() noexcept; + /** + * Check if the array is at the end. + */ + simdjson_warn_unused simdjson_inline bool at_end() const noexcept; + private: value_iterator iter{}; @@ -57753,7 +64034,6 @@ namespace simdjson { template<> struct simdjson_result : public haswell::implementation_simdjson_result_base { -public: simdjson_inline simdjson_result(haswell::ondemand::array_iterator &&value) noexcept; ///< @private simdjson_inline simdjson_result(error_code error) noexcept; ///< @private simdjson_inline simdjson_result() noexcept = default; @@ -57766,6 +64046,8 @@ struct simdjson_result : public haswell::impl simdjson_inline bool operator==(const simdjson_result &) const noexcept; simdjson_inline bool operator!=(const simdjson_result &) const noexcept; simdjson_inline simdjson_result &operator++() noexcept; + + simdjson_warn_unused simdjson_inline bool at_end() const noexcept; }; } // namespace simdjson @@ -57893,7 +64175,7 @@ class document { * @returns INCORRECT_TYPE if the JSON value is not a string. Otherwise, we return SUCCESS. */ template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; /** * Cast this JSON value to a string. * @@ -57959,7 +64241,7 @@ class document { */ template simdjson_inline simdjson_result get() & -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept @@ -57982,7 +64264,7 @@ class document { */ template simdjson_inline simdjson_result get() && -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept @@ -58000,18 +64282,18 @@ class document { * Be mindful that the document instance must remain in scope while you are accessing object, array and value instances. * * @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized. - * @returns INCORRECT_TYPE If the JSON value is not an object. + * @returns INCORRECT_TYPE If the JSON value is of the given type. * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. */ template - simdjson_inline error_code get(T &out) & -#if SIMDJSON_SUPPORTS_DESERIALIZATION + simdjson_warn_unused simdjson_inline error_code get(T &out) & +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept #endif { -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS if constexpr (custom_deserializable) { return deserialize(*this, out); } else { @@ -58023,7 +64305,7 @@ class document { static_cast(out); // to get rid of unused errors return UNINITIALIZED; } -#else // SIMDJSON_SUPPORTS_DESERIALIZATION +#else // SIMDJSON_SUPPORTS_CONCEPTS // Unless the simdjson library or the user provides an inline implementation, calling this method should // immediately fail. static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " @@ -58033,7 +64315,7 @@ class document { " You may also add support for custom types, see our documentation."); static_cast(out); // to get rid of unused errors return UNINITIALIZED; -#endif // SIMDJSON_SUPPORTS_DESERIALIZATION +#endif // SIMDJSON_SUPPORTS_CONCEPTS } /** @overload template error_code get(T &out) & noexcept */ @@ -58098,7 +64380,7 @@ class document { * time it parses a document or when it is destroyed. * @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string. */ - simdjson_inline operator std::string_view() noexcept(false); + simdjson_inline operator std::string_view() noexcept(false) simdjson_lifetime_bound; /** * Cast this JSON value to a raw_json_string. * @@ -58107,7 +64389,7 @@ class document { * @returns A pointer to the raw JSON for the given string. * @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string. */ - simdjson_inline operator raw_json_string() noexcept(false); + simdjson_inline operator raw_json_string() noexcept(false) simdjson_lifetime_bound; /** * Cast this JSON value to a bool. * @@ -58257,11 +64539,27 @@ class document { * E.g., you must still call "is_null()" to check that a value is null even if * "type()" returns json_type::null. * + * The answer can be one of + * simdjson::ondemand::json_type::object, + * simdjson::ondemand::json_type::array, + * simdjson::ondemand::json_type::string, + * simdjson::ondemand::json_type::number, + * simdjson::ondemand::json_type::boolean, + * simdjson::ondemand::json_type::null. + * + * Starting with simdjson 4.0, this function will return simdjson::ondemand::json_type::unknown + * given a bad token. + * This allows you to identify a case such as {"key": NaN} and identify the NaN value. + * The simdjson::ondemand::json_type::unknown value should only happen with non-valid JSON. + * * NOTE: If you're only expecting a value to be one type (a typical case), it's generally * better to just call .get_double, .get_string, etc. and check for INCORRECT_TYPE (or just * let it throw an exception). * - * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". + * Prior to simdjson 4.0, this function would return an error given a bad token. + * Starting with simdjson 4.0, it will return simdjson::ondemand::json_type::unknown. + * This allows you to identify a case such as {"key": NaN} and identify the NaN value. + * The simdjson::ondemand::json_type::unknown value should only happen with non-valid JSON. */ simdjson_inline simdjson_result type() noexcept; @@ -58485,11 +64783,41 @@ class document { * the JSON document. */ simdjson_inline simdjson_result raw_json() noexcept; + +#if SIMDJSON_STATIC_REFLECTION + /** + * Extract only specific fields from the JSON object into a struct. + * + * This allows selective deserialization of only the fields you need, + * potentially improving performance by skipping unwanted fields. + * + * Example: + * ```c++ + * struct Car { + * std::string make; + * std::string model; + * int year; + * double price; + * }; + * + * Car car; + * doc.extract_into<"make", "model">(car); + * // Only 'make' and 'model' fields are extracted from JSON + * ``` + * + * @tparam FieldNames Compile-time string literals specifying which fields to extract + * @param out The output struct to populate with selected fields + * @returns SUCCESS on success, or an error code if a required field is missing or has wrong type + */ + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept; +#endif // SIMDJSON_STATIC_REFLECTION protected: /** * Consumes the document. */ - simdjson_inline error_code consume() noexcept; + simdjson_warn_unused simdjson_inline error_code consume() noexcept; simdjson_inline document(ondemand::json_iterator &&iter) noexcept; simdjson_inline const uint8_t *text(uint32_t idx) const noexcept; @@ -58542,7 +64870,7 @@ class document_reference { simdjson_inline simdjson_result get_double_in_string() noexcept; simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; simdjson_inline simdjson_result get_wobbly_string() noexcept; simdjson_inline simdjson_result get_raw_json_string() noexcept; simdjson_inline simdjson_result get_bool() noexcept; @@ -58551,7 +64879,7 @@ class document_reference { simdjson_inline simdjson_result is_null() noexcept; template simdjson_inline simdjson_result get() & -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept @@ -58564,7 +64892,7 @@ class document_reference { } template simdjson_inline simdjson_result get() && -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept @@ -58586,14 +64914,14 @@ class document_reference { * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. */ template - simdjson_inline error_code get(T &out) & -#if SIMDJSON_SUPPORTS_DESERIALIZATION + simdjson_warn_unused simdjson_inline error_code get(T &out) & +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept #endif { -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS if constexpr (custom_deserializable) { return deserialize(*this, out); } else { @@ -58605,7 +64933,7 @@ class document_reference { static_cast(out); // to get rid of unused errors return UNINITIALIZED; } -#else // SIMDJSON_SUPPORTS_DESERIALIZATION +#else // SIMDJSON_SUPPORTS_CONCEPTS // Unless the simdjson library or the user provides an inline implementation, calling this method should // immediately fail. static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " @@ -58615,12 +64943,17 @@ class document_reference { " You may also add support for custom types, see our documentation."); static_cast(out); // to get rid of unused errors return UNINITIALIZED; -#endif // SIMDJSON_SUPPORTS_DESERIALIZATION +#endif // SIMDJSON_SUPPORTS_CONCEPTS } /** @overload template error_code get(T &out) & noexcept */ template simdjson_inline error_code get(T &out) && noexcept; simdjson_inline simdjson_result raw_json() noexcept; +#if SIMDJSON_STATIC_REFLECTION + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept; +#endif // SIMDJSON_STATIC_REFLECTION simdjson_inline operator document&() const noexcept; #if SIMDJSON_EXCEPTIONS template @@ -58689,7 +65022,7 @@ struct simdjson_result : public haswell::implementa simdjson_inline simdjson_result get_double_in_string() noexcept; simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; simdjson_inline simdjson_result get_wobbly_string() noexcept; simdjson_inline simdjson_result get_raw_json_string() noexcept; simdjson_inline simdjson_result get_bool() noexcept; @@ -58702,6 +65035,9 @@ struct simdjson_result : public haswell::implementa template simdjson_inline error_code get(T &out) & noexcept; template simdjson_inline error_code get(T &out) && noexcept; #if SIMDJSON_EXCEPTIONS + + using haswell::implementation_simdjson_result_base::operator*; + using haswell::implementation_simdjson_result_base::operator->; template ::value == false>::type> explicit simdjson_inline operator T() noexcept(false); simdjson_inline operator haswell::ondemand::array() & noexcept(false); @@ -58741,6 +65077,11 @@ struct simdjson_result : public haswell::implementa simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; +#if SIMDJSON_STATIC_REFLECTION + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept; +#endif // SIMDJSON_STATIC_REFLECTION }; @@ -58767,7 +65108,7 @@ struct simdjson_result : public haswell:: simdjson_inline simdjson_result get_double_in_string() noexcept; simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; simdjson_inline simdjson_result get_wobbly_string() noexcept; simdjson_inline simdjson_result get_raw_json_string() noexcept; simdjson_inline simdjson_result get_bool() noexcept; @@ -58818,6 +65159,11 @@ struct simdjson_result : public haswell:: simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; +#if SIMDJSON_STATIC_REFLECTION + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept; +#endif // SIMDJSON_STATIC_REFLECTION }; @@ -58960,6 +65306,7 @@ class document_stream { * Default constructor. */ simdjson_inline iterator() noexcept; + simdjson_inline iterator(const iterator &other) noexcept = default; /** * Get the current document (or error). */ @@ -58973,6 +65320,7 @@ class document_stream { * @param other the end iterator to compare to. */ simdjson_inline bool operator!=(const iterator &other) const noexcept; + simdjson_inline bool operator==(const iterator &other) const noexcept; /** * @private * @@ -59016,6 +65364,11 @@ class document_stream { */ inline error_code error() const noexcept; + /** + * Returns whether the iterator is at the end. + */ + inline bool at_end() const noexcept; + private: simdjson_inline iterator(document_stream *s, bool finished) noexcept; /** The document_stream we're iterating through. */ @@ -59027,6 +65380,7 @@ class document_stream { friend class document_stream; friend class json_iterator; }; + using iterator = document_stream::iterator; /** * Start iterating the documents in the stream. @@ -59290,6 +65644,9 @@ struct simdjson_result : public haswell::implementatio /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */ +/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION && SIMDJSON_SUPPORTS_CONCEPTS */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_string_builder.h" // for constevalutil::fixed_string */ +/* amalgamation skipped (editor-only): #endif */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { @@ -59487,11 +65844,71 @@ class object { */ simdjson_inline simdjson_result raw_json() noexcept; +#if SIMDJSON_SUPPORTS_CONCEPTS + /** + * Get this object as the given type. + * + * @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized. + * @returns INCORRECT_TYPE If the JSON object is not of the given type. + * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. + */ + template + simdjson_warn_unused simdjson_inline error_code get(T &out) + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) { + static_assert(custom_deserializable); + return deserialize(*this, out); + } + /** + * Get this array as the given type. + * + * @returns A value of the given type, parsed from the JSON. + * @returns INCORRECT_TYPE If the JSON value is not the given type. + */ + template + simdjson_inline simdjson_result get() + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) + { + static_assert(std::is_default_constructible::value, "The specified type is not default constructible."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } + +#if SIMDJSON_STATIC_REFLECTION + /** + * Extract only specific fields from the JSON object into a struct. + * + * This allows selective deserialization of only the fields you need, + * potentially improving performance by skipping unwanted fields. + * + * Example: + * ```c++ + * struct Car { + * std::string make; + * std::string model; + * int year; + * double price; + * }; + * + * Car car; + * object.extract_into<"make", "model">(car); + * // Only 'make' and 'model' fields are extracted from JSON + * ``` + * + * @tparam FieldNames Compile-time string literals specifying which fields to extract + * @param out The output struct to populate with selected fields + * @returns SUCCESS on success, or an error code if a required field is missing or has wrong type + */ + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept; +#endif // SIMDJSON_STATIC_REFLECTION +#endif // SIMDJSON_SUPPORTS_CONCEPTS protected: /** * Go to the end of the object, no matter where you are right now. */ - simdjson_inline error_code consume() noexcept; + simdjson_warn_unused simdjson_inline error_code consume() noexcept; static simdjson_inline simdjson_result start(value_iterator &iter) noexcept; static simdjson_inline simdjson_result start_root(value_iterator &iter) noexcept; static simdjson_inline simdjson_result started(value_iterator &iter) noexcept; @@ -59530,12 +65947,42 @@ struct simdjson_result : public haswell::implementati simdjson_inline simdjson_result operator[](std::string_view key) && noexcept; simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; - inline simdjson_result reset() noexcept; inline simdjson_result is_empty() noexcept; inline simdjson_result count_fields() & noexcept; inline simdjson_result raw_json() noexcept; + #if SIMDJSON_SUPPORTS_CONCEPTS + // TODO: move this code into object-inl.h + template + simdjson_inline simdjson_result get() noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + return first; + } + return first.get(); + } + template + simdjson_warn_unused simdjson_inline error_code get(T& out) noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + out = first; + } else { + SIMDJSON_TRY( first.get(out) ); + } + return SUCCESS; + } + +#if SIMDJSON_STATIC_REFLECTION + // TODO: move this code into object-inl.h + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) noexcept { + if (error()) { return error(); } + return first.extract_into(out); + } +#endif // SIMDJSON_STATIC_REFLECTION +#endif // SIMDJSON_SUPPORTS_CONCEPTS }; } // namespace simdjson @@ -59664,6 +66111,20 @@ inline simdjson_result to_json_string(simdjson_result to_json_string(simdjson_result x); inline simdjson_result to_json_string(simdjson_result x); inline simdjson_result to_json_string(simdjson_result x); + +#if SIMDJSON_STATIC_REFLECTION +/** + * Create a JSON string from any user-defined type using static reflection. + * Only available when SIMDJSON_STATIC_REFLECTION is enabled. + */ +template + requires(!std::same_as && + !std::same_as && + !std::same_as && + !std::same_as) +inline std::string to_json_string(const T& obj); +#endif + } // namespace simdjson /** @@ -59735,28 +66196,30 @@ inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result #include +#if SIMDJSON_STATIC_REFLECTION +#include +// #include // for std::define_static_string - header not available yet +#endif namespace simdjson { -template -constexpr bool require_custom_serialization = false; ////////////////////////////// // Number deserialization ////////////////////////////// template - requires(!require_custom_serialization) error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { using limits = std::numeric_limits; @@ -59770,7 +66233,6 @@ error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { } template - requires(!require_custom_serialization) error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { double x; SIMDJSON_TRY(val.get_double().get(x)); @@ -59779,7 +66241,6 @@ error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { } template - requires(!require_custom_serialization) error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { using limits = std::numeric_limits; @@ -59792,8 +66253,23 @@ error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { return SUCCESS; } +////////////////////////////// +// String deserialization +////////////////////////////// + +// just a character! +error_code tag_invoke(deserialize_tag, auto &val, char &out) noexcept { + std::string_view x; + SIMDJSON_TRY(val.get_string().get(x)); + if(x.size() != 1) { + return INCORRECT_TYPE; + } + out = x[0]; + return SUCCESS; +} + +// any string-like type (can be constructed from std::string_view) template - requires(!require_custom_serialization) error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(std::is_nothrow_constructible_v) { std::string_view str; SIMDJSON_TRY(val.get_string().get(str)); @@ -59810,7 +66286,6 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(std::is_nothr * doc.get>(). */ template - requires(!require_custom_serialization) error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { using value_type = typename std::remove_cvref_t::value_type; static_assert( @@ -59819,9 +66294,13 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { static_assert( std::is_default_constructible_v, "The specified type inside the container must default constructible."); - haswell::ondemand::array arr; - SIMDJSON_TRY(val.get_array().get(arr)); + if constexpr (std::is_same_v, haswell::ondemand::array>) { + arr = val; + } else { + SIMDJSON_TRY(val.get_array().get(arr)); + } + for (auto v : arr) { if constexpr (concepts::returns_reference) { if (auto const err = v.get().get(concepts::emplace_one(out)); @@ -59852,7 +66331,6 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { * string-keyed types. */ template - requires(!require_custom_serialization) error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { using value_type = typename std::remove_cvref_t::mapped_type; static_assert( @@ -59878,7 +66356,45 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { return SUCCESS; } +template +error_code tag_invoke(deserialize_tag, haswell::ondemand::object &obj, T &out) noexcept { + using value_type = typename std::remove_cvref_t::mapped_type; + out.clear(); + for (auto field : obj) { + std::string_view key; + SIMDJSON_TRY(field.unescaped_key().get(key)); + + haswell::ondemand::value value_obj; + SIMDJSON_TRY(field.value().get(value_obj)); + + value_type this_value; + SIMDJSON_TRY(value_obj.get(this_value)); + out.emplace(typename T::key_type(key), std::move(this_value)); + } + return SUCCESS; +} + +template +error_code tag_invoke(deserialize_tag, haswell::ondemand::value &val, T &out) noexcept { + haswell::ondemand::object obj; + SIMDJSON_TRY(val.get_object().get(obj)); + return simdjson::deserialize(obj, out); +} + +template +error_code tag_invoke(deserialize_tag, haswell::ondemand::document &doc, T &out) noexcept { + haswell::ondemand::object obj; + SIMDJSON_TRY(doc.get_object().get(obj)); + return simdjson::deserialize(obj, out); +} + +template +error_code tag_invoke(deserialize_tag, haswell::ondemand::document_reference &doc, T &out) noexcept { + haswell::ondemand::object obj; + SIMDJSON_TRY(doc.get_object().get(obj)); + return simdjson::deserialize(obj, out); +} /** @@ -59896,7 +66412,6 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { * @return status of the conversion */ template - requires(!require_custom_serialization) error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deserializable::element_type, ValT>) { using element_type = typename std::remove_cvref_t::element_type; @@ -59921,17 +66436,17 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deser /** * This CPO (Customization Point Object) will help deserialize into optional types. */ -template - requires(!require_custom_serialization) -error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deserializable::value_type, ValT>) { +template +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept(nothrow_deserializable::value_type, decltype(val)>) { using value_type = typename std::remove_cvref_t::value_type; - static_assert( - deserializable, - "The specified type inside the unique_ptr must itself be deserializable"); - static_assert( - std::is_default_constructible_v, - "The specified type inside the unique_ptr must default constructible."); + // Check if the value is null + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); // Set to nullopt + return SUCCESS; + } if (!out) { out.emplace(); @@ -59940,10 +66455,329 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deser return SUCCESS; } + +#if SIMDJSON_STATIC_REFLECTION + + +template +constexpr bool user_defined_type = (std::is_class_v +&& !std::is_same_v && !std::is_same_v && !concepts::optional_type && +!concepts::appendable_containers); + + +template + requires(user_defined_type && std::is_class_v) +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept { + haswell::ondemand::object obj; + if constexpr (std::is_same_v, haswell::ondemand::object>) { + obj = val; + } else { + SIMDJSON_TRY(val.get_object().get(obj)); + } + template for (constexpr auto mem : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) { + if constexpr (!std::meta::is_const(mem) && std::meta::is_public(mem)) { + constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem)); + if constexpr (concepts::optional_type) { + // for optional members, it's ok if the key is missing + auto error = obj[key].get(out.[:mem:]); + if (error && error != NO_SUCH_FIELD) { + if(error == NO_SUCH_FIELD) { + out.[:mem:].reset(); + continue; + } + return error; + } + } else { + // for non-optional members, the key must be present + SIMDJSON_TRY(obj[key].get(out.[:mem:])); + } + } + }; + return simdjson::SUCCESS; +} + +// Support for enum deserialization - deserialize from string representation using expand approach from P2996R12 +template + requires(std::is_enum_v) +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept { +#if SIMDJSON_STATIC_REFLECTION + std::string_view str; + SIMDJSON_TRY(val.get_string().get(str)); + constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T)); + template for (constexpr auto enum_val : enumerators) { + if (str == std::meta::identifier_of(enum_val)) { + out = [:enum_val:]; + return SUCCESS; + } + }; + + return INCORRECT_TYPE; +#else + // Fallback: deserialize as integer if reflection not available + std::underlying_type_t int_val; + SIMDJSON_TRY(val.get(int_val)); + out = static_cast(int_val); + return SUCCESS; +#endif +} + +template + requires(user_defined_type>) +error_code tag_invoke(deserialize_tag, simdjson_value &val, std::unique_ptr &out) noexcept { + if (!out) { + out = std::make_unique(); + if (!out) { + return MEMALLOC; + } + } + if (auto err = val.get(*out)) { + out.reset(); + return err; + } + return SUCCESS; +} + +template + requires(user_defined_type>) +error_code tag_invoke(deserialize_tag, simdjson_value &val, std::shared_ptr &out) noexcept { + if (!out) { + out = std::make_shared(); + if (!out) { + return MEMALLOC; + } + } + if (auto err = val.get(*out)) { + out.reset(); + return err; + } + return SUCCESS; +} + +#endif // SIMDJSON_STATIC_REFLECTION + +//////////////////////////////////////// +// Unique pointers +//////////////////////////////////////// +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_unique(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_bool().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_unique(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_int64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_unique(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_uint64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_unique(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_double().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_unique(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_string().get(*out)); + return SUCCESS; +} + + +//////////////////////////////////////// +// Shared pointers +//////////////////////////////////////// +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_shared(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_bool().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_shared(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_int64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_shared(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_uint64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_shared(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_double().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_shared(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_string().get(*out)); + return SUCCESS; +} + + +//////////////////////////////////////// +// Explicit optional specializations +//////////////////////////////////////// + +//////////////////////////////////////// +// Explicit smart pointer specializations for string and int types +//////////////////////////////////////// +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + // Check if the value is null + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); // Set to nullptr + return SUCCESS; + } + + if (!out) { + out = std::make_unique(); + } + std::string_view str; + SIMDJSON_TRY(val.get_string().get(str)); + *out = std::string{str}; + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + // Check if the value is null + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); // Set to nullptr + return SUCCESS; + } + + if (!out) { + out = std::make_shared(); + } + std::string_view str; + SIMDJSON_TRY(val.get_string().get(str)); + *out = std::string{str}; + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + // Check if the value is null + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); // Set to nullptr + return SUCCESS; + } + + if (!out) { + out = std::make_unique(); + } + int64_t temp; + SIMDJSON_TRY(val.get_int64().get(temp)); + *out = static_cast(temp); + return SUCCESS; +} + } // namespace simdjson #endif // SIMDJSON_ONDEMAND_DESERIALIZE_H -#endif // SIMDJSON_SUPPORTS_DESERIALIZATION +#endif // SIMDJSON_SUPPORTS_CONCEPTS /* end file simdjson/generic/ondemand/std_deserialize.h for haswell */ // Inline definitions @@ -60036,7 +66870,7 @@ simdjson_inline simdjson_result array::begin() noexcept { simdjson_inline simdjson_result array::end() noexcept { return array_iterator(iter); } -simdjson_inline error_code array::consume() noexcept { +simdjson_warn_unused simdjson_warn_unused simdjson_inline error_code array::consume() noexcept { auto error = iter.json_iter().skip_child(iter.depth()-1); if(error) { iter.abandon(); } return error; @@ -60227,6 +67061,9 @@ simdjson_inline array_iterator &array_iterator::operator++() noexcept { return *this; } +simdjson_inline bool array_iterator::at_end() const noexcept { + return iter.at_end(); +} } // namespace ondemand } // namespace haswell } // namespace simdjson @@ -60263,7 +67100,9 @@ simdjson_inline simdjson_result &simdjson_res ++(first); return *this; } - +simdjson_inline bool simdjson_result::at_end() const noexcept { + return !first.iter.is_valid() || first.at_end(); +} } // namespace simdjson #endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_INL_H @@ -60320,7 +67159,7 @@ simdjson_inline simdjson_result value::get_string(bool allow_r return iter.get_string(allow_replacement); } template -simdjson_inline error_code value::get_string(string_type& receiver, bool allow_replacement) noexcept { +simdjson_warn_unused simdjson_inline error_code value::get_string(string_type& receiver, bool allow_replacement) noexcept { return iter.get_string(receiver, allow_replacement); } simdjson_inline simdjson_result value::get_wobbly_string() noexcept { @@ -60362,15 +67201,15 @@ template<> simdjson_inline simdjson_result value::get() noexcept { retu template<> simdjson_inline simdjson_result value::get() noexcept { return get_bool(); } -template<> simdjson_inline error_code value::get(array& out) noexcept { return get_array().get(out); } -template<> simdjson_inline error_code value::get(object& out) noexcept { return get_object().get(out); } -template<> simdjson_inline error_code value::get(raw_json_string& out) noexcept { return get_raw_json_string().get(out); } -template<> simdjson_inline error_code value::get(std::string_view& out) noexcept { return get_string(false).get(out); } -template<> simdjson_inline error_code value::get(number& out) noexcept { return get_number().get(out); } -template<> simdjson_inline error_code value::get(double& out) noexcept { return get_double().get(out); } -template<> simdjson_inline error_code value::get(uint64_t& out) noexcept { return get_uint64().get(out); } -template<> simdjson_inline error_code value::get(int64_t& out) noexcept { return get_int64().get(out); } -template<> simdjson_inline error_code value::get(bool& out) noexcept { return get_bool().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(array& out) noexcept { return get_array().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(object& out) noexcept { return get_object().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(raw_json_string& out) noexcept { return get_raw_json_string().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(std::string_view& out) noexcept { return get_string(false).get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(number& out) noexcept { return get_number().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(double& out) noexcept { return get_double().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(uint64_t& out) noexcept { return get_uint64().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(int64_t& out) noexcept { return get_int64().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(bool& out) noexcept { return get_bool().get(out); } #if SIMDJSON_EXCEPTIONS template @@ -60967,7 +67806,7 @@ simdjson_inline simdjson_result document::get_string(bool allo return get_root_value_iterator().get_root_string(true, allow_replacement); } template -simdjson_inline error_code document::get_string(string_type& receiver, bool allow_replacement) noexcept { +simdjson_warn_unused simdjson_inline error_code document::get_string(string_type& receiver, bool allow_replacement) noexcept { return get_root_value_iterator().get_root_string(receiver, true, allow_replacement); } simdjson_inline simdjson_result document::get_wobbly_string() noexcept { @@ -60993,15 +67832,15 @@ template<> simdjson_inline simdjson_result document::get() & noexcept { template<> simdjson_inline simdjson_result document::get() & noexcept { return get_bool(); } template<> simdjson_inline simdjson_result document::get() & noexcept { return get_value(); } -template<> simdjson_inline error_code document::get(array& out) & noexcept { return get_array().get(out); } -template<> simdjson_inline error_code document::get(object& out) & noexcept { return get_object().get(out); } -template<> simdjson_inline error_code document::get(raw_json_string& out) & noexcept { return get_raw_json_string().get(out); } -template<> simdjson_inline error_code document::get(std::string_view& out) & noexcept { return get_string(false).get(out); } -template<> simdjson_inline error_code document::get(double& out) & noexcept { return get_double().get(out); } -template<> simdjson_inline error_code document::get(uint64_t& out) & noexcept { return get_uint64().get(out); } -template<> simdjson_inline error_code document::get(int64_t& out) & noexcept { return get_int64().get(out); } -template<> simdjson_inline error_code document::get(bool& out) & noexcept { return get_bool().get(out); } -template<> simdjson_inline error_code document::get(value& out) & noexcept { return get_value().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(array& out) & noexcept { return get_array().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(object& out) & noexcept { return get_object().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(raw_json_string& out) & noexcept { return get_raw_json_string().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(std::string_view& out) & noexcept { return get_string(false).get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(double& out) & noexcept { return get_double().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(uint64_t& out) & noexcept { return get_uint64().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(int64_t& out) & noexcept { return get_int64().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(bool& out) & noexcept { return get_bool().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(value& out) & noexcept { return get_value().get(out); } template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_raw_json_string(); } template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_string(false); } @@ -61021,8 +67860,8 @@ simdjson_inline document::operator object() & noexcept(false) { return get_objec simdjson_inline document::operator uint64_t() noexcept(false) { return get_uint64(); } simdjson_inline document::operator int64_t() noexcept(false) { return get_int64(); } simdjson_inline document::operator double() noexcept(false) { return get_double(); } -simdjson_inline document::operator std::string_view() noexcept(false) { return get_string(false); } -simdjson_inline document::operator raw_json_string() noexcept(false) { return get_raw_json_string(); } +simdjson_inline document::operator std::string_view() noexcept(false) simdjson_lifetime_bound { return get_string(false); } +simdjson_inline document::operator raw_json_string() noexcept(false) simdjson_lifetime_bound { return get_raw_json_string(); } simdjson_inline document::operator bool() noexcept(false) { return get_bool(); } simdjson_inline document::operator value() noexcept(false) { return get_value(); } @@ -61071,7 +67910,7 @@ simdjson_inline simdjson_result document::operator[](const char *key) & n return start_or_resume_object()[key]; } -simdjson_inline error_code document::consume() noexcept { +simdjson_warn_unused simdjson_inline error_code document::consume() noexcept { bool scalar = false; auto error = is_scalar().get(scalar); if(error) { return error; } @@ -61173,6 +68012,54 @@ simdjson_inline simdjson_result document::at_path(std::string_view json_p } } + + +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION + +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code document::extract_into(T& out) & noexcept { + // Helper to check if a field name matches any of the requested fields + auto should_extract = [](std::string_view field_name) constexpr -> bool { + return ((FieldNames.view() == field_name) || ...); + }; + + // Iterate through all members of T using reflection + template for (constexpr auto mem : std::define_static_array( + std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) { + + if constexpr (!std::meta::is_const(mem) && std::meta::is_public(mem)) { + constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem)); + + // Only extract this field if it's in our list of requested fields + if constexpr (should_extract(key)) { + // Try to find and extract the field + if constexpr (concepts::optional_type) { + // For optional fields, it's ok if they're missing + auto field_result = find_field_unordered(key); + if (!field_result.error()) { + auto error = field_result.get(out.[:mem:]); + if (error && error != NO_SUCH_FIELD) { + return error; + } + } else if (field_result.error() != NO_SUCH_FIELD) { + return field_result.error(); + } else { + out.[:mem:].reset(); + } + } else { + // For required fields (in the requested list), fail if missing + SIMDJSON_TRY((*this)[key].get(out.[:mem:])); + } + } + } + }; + + return SUCCESS; +} + +#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION + } // namespace ondemand } // namespace haswell } // namespace simdjson @@ -61280,7 +68167,7 @@ simdjson_inline simdjson_result simdjson_result -simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { if (error()) { return error(); } return first.get_string(receiver, allow_replacement); } @@ -61316,12 +68203,12 @@ simdjson_deprecated simdjson_inline simdjson_result simdjson_result(first).get(); } template -simdjson_inline error_code simdjson_result::get(T &out) & noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) & noexcept { if (error()) { return error(); } return first.get(out); } template -simdjson_inline error_code simdjson_result::get(T &out) && noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) && noexcept { if (error()) { return error(); } return std::forward(first).get(out); } @@ -61331,8 +68218,8 @@ template<> simdjson_deprecated simdjson_inline simdjson_result(first); } -template<> simdjson_inline error_code simdjson_result::get(haswell::ondemand::document &out) & noexcept = delete; -template<> simdjson_inline error_code simdjson_result::get(haswell::ondemand::document &out) && noexcept { +template<> simdjson_warn_unused simdjson_inline error_code simdjson_result::get(haswell::ondemand::document &out) & noexcept = delete; +template<> simdjson_warn_unused simdjson_inline error_code simdjson_result::get(haswell::ondemand::document &out) && noexcept { if (error()) { return error(); } out = std::forward(first); return SUCCESS; @@ -61450,6 +68337,15 @@ simdjson_inline simdjson_result simdjson_result + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code simdjson_result::extract_into(T& out) & noexcept { + if (error()) { return error(); } + return first.extract_into(out); +} +#endif // SIMDJSON_STATIC_REFLECTION + } // namespace simdjson @@ -61484,7 +68380,7 @@ simdjson_inline simdjson_result document_reference::get_double() noexcep simdjson_inline simdjson_result document_reference::get_double_in_string() noexcept { return doc->get_root_value_iterator().get_root_double(false); } simdjson_inline simdjson_result document_reference::get_string(bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(false, allow_replacement); } template -simdjson_inline error_code document_reference::get_string(string_type& receiver, bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(receiver, false, allow_replacement); } +simdjson_warn_unused simdjson_inline error_code document_reference::get_string(string_type& receiver, bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(receiver, false, allow_replacement); } simdjson_inline simdjson_result document_reference::get_wobbly_string() noexcept { return doc->get_root_value_iterator().get_root_wobbly_string(false); } simdjson_inline simdjson_result document_reference::get_raw_json_string() noexcept { return doc->get_root_value_iterator().get_root_raw_json_string(false); } simdjson_inline simdjson_result document_reference::get_bool() noexcept { return doc->get_root_value_iterator().get_root_bool(false); } @@ -61537,7 +68433,13 @@ simdjson_inline simdjson_result document_reference::at_pointer(std::strin simdjson_inline simdjson_result document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); } simdjson_inline simdjson_result document_reference::raw_json() noexcept { return doc->raw_json();} simdjson_inline document_reference::operator document&() const noexcept { return *doc; } - +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code document_reference::extract_into(T& out) & noexcept { + return doc->extract_into(out); +} +#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION } // namespace ondemand } // namespace haswell } // namespace simdjson @@ -61634,7 +68536,7 @@ simdjson_inline simdjson_result simdjson_result -simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { if (error()) { return error(); } return first.get_string(receiver, allow_replacement); } @@ -61669,12 +68571,12 @@ simdjson_inline simdjson_result simdjson_result(first).get(); } template -simdjson_inline error_code simdjson_result::get(T &out) & noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) & noexcept { if (error()) { return error(); } return first.get(out); } template -simdjson_inline error_code simdjson_result::get(T &out) && noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) && noexcept { if (error()) { return error(); } return std::forward(first).get(out); } @@ -61691,13 +68593,13 @@ simdjson_inline simdjson_result simdjson_result -simdjson_inline error_code simdjson_result::get(haswell::ondemand::document_reference &out) & noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(haswell::ondemand::document_reference &out) & noexcept { if (error()) { return error(); } out = first; return SUCCESS; } template <> -simdjson_inline error_code simdjson_result::get(haswell::ondemand::document_reference &out) && noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(haswell::ondemand::document_reference &out) && noexcept { if (error()) { return error(); } out = first; return SUCCESS; @@ -61785,7 +68687,14 @@ simdjson_inline simdjson_result simdjson_result + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code simdjson_result::extract_into(T& out) & noexcept { + if (error()) { return error(); } + return first.extract_into(out); +} +#endif // SIMDJSON_STATIC_REFLECTION } // namespace simdjson #endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H @@ -61976,10 +68885,19 @@ simdjson_inline document_stream::iterator& document_stream::iterator::operator++ return *this; } +simdjson_inline bool document_stream::iterator::at_end() const noexcept { + return finished; +} + + simdjson_inline bool document_stream::iterator::operator!=(const document_stream::iterator &other) const noexcept { return finished != other.finished; } +simdjson_inline bool document_stream::iterator::operator==(const document_stream::iterator &other) const noexcept { + return finished == other.finished; +} + simdjson_inline document_stream::iterator document_stream::begin() noexcept { start(); // If there are no documents, we're finished. @@ -62097,7 +69015,10 @@ inline void document_stream::next_document() noexcept { // Always set depth=1 at the start of document doc.iter._depth = 1; // consume comma if comma separated is allowed - if (allow_comma_separated) { doc.iter.consume_character(','); } + if (allow_comma_separated) { + error_code ignored = doc.iter.consume_character(','); + static_cast(ignored); // ignored on purpose + } // Resets the string buffer at the beginning, thus invalidating the strings. doc.iter._string_buf_loc = parser->string_buf.get(); doc.iter._root = doc.iter.position(); @@ -62343,7 +69264,7 @@ simdjson_inline simdjson_result simdjson_result -simdjson_inline error_code simdjson_result::unescaped_key(string_type &receiver, bool allow_replacement) noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::unescaped_key(string_type &receiver, bool allow_replacement) noexcept { if (error()) { return error(); } return first.unescaped_key(receiver, allow_replacement); } @@ -62579,6 +69500,8 @@ simdjson_inline void json_iterator::assert_valid_position(token_position positio #ifndef SIMDJSON_CLANG_VISUAL_STUDIO SIMDJSON_ASSUME( position >= &parser->implementation->structural_indexes[0] ); SIMDJSON_ASSUME( position < &parser->implementation->structural_indexes[parser->implementation->n_structural_indexes] ); +#else + (void)position; // Suppress unused parameter warning #endif } @@ -62703,7 +69626,7 @@ simdjson_inline uint8_t *&json_iterator::string_buf_loc() noexcept { return _string_buf_loc; } -simdjson_inline error_code json_iterator::report_error(error_code _error, const char *message) noexcept { +simdjson_warn_unused simdjson_inline error_code json_iterator::report_error(error_code _error, const char *message) noexcept { SIMDJSON_ASSUME(_error != SUCCESS && _error != UNINITIALIZED && _error != INCORRECT_TYPE && _error != NO_SUCH_FIELD); logger::log_error(*this, message); error = _error; @@ -62747,7 +69670,7 @@ simdjson_inline void json_iterator::reenter_child(token_position position, depth _depth = child_depth; } -simdjson_inline error_code json_iterator::consume_character(char c) noexcept { +simdjson_warn_unused simdjson_inline error_code json_iterator::consume_character(char c) noexcept { if (*peek() == c) { return_current_and_advance(); return SUCCESS; @@ -62770,7 +69693,7 @@ simdjson_inline void json_iterator::set_start_position(depth_t depth, token_posi #endif -simdjson_inline error_code json_iterator::optional_error(error_code _error, const char *message) noexcept { +simdjson_warn_unused simdjson_inline error_code json_iterator::optional_error(error_code _error, const char *message) noexcept { SIMDJSON_ASSUME(_error == INCORRECT_TYPE || _error == NO_SUCH_FIELD); logger::log_error(*this, message); return _error; @@ -63165,6 +70088,10 @@ inline void log_line(const json_iterator &iter, token_position index, depth_t de /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value-inl.h" */ +/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_string_builder.h" // for constevalutil::fixed_string */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #endif */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { @@ -63222,7 +70149,7 @@ simdjson_inline simdjson_result object::start_root(value_iterator &iter) SIMDJSON_TRY( iter.start_root_object().error() ); return object(iter); } -simdjson_inline error_code object::consume() noexcept { +simdjson_warn_unused simdjson_inline error_code object::consume() noexcept { if(iter.is_at_key()) { /** * whenever you are pointing at a key, calling skip_child() is @@ -63351,6 +70278,52 @@ simdjson_inline simdjson_result object::reset() & noexcept { return iter.reset_object(); } +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION + +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code object::extract_into(T& out) & noexcept { + // Helper to check if a field name matches any of the requested fields + auto should_extract = [](std::string_view field_name) constexpr -> bool { + return ((FieldNames.view() == field_name) || ...); + }; + + // Iterate through all members of T using reflection + template for (constexpr auto mem : std::define_static_array( + std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) { + + if constexpr (!std::meta::is_const(mem) && std::meta::is_public(mem)) { + constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem)); + + // Only extract this field if it's in our list of requested fields + if constexpr (should_extract(key)) { + // Try to find and extract the field + if constexpr (concepts::optional_type) { + // For optional fields, it's ok if they're missing + auto field_result = find_field_unordered(key); + if (!field_result.error()) { + auto error = field_result.get(out.[:mem:]); + if (error && error != NO_SUCH_FIELD) { + return error; + } + } else if (field_result.error() != NO_SUCH_FIELD) { + return field_result.error(); + } else { + out.[:mem:].reset(); + } + } else { + // For required fields (in the requested list), fail if missing + SIMDJSON_TRY((*this)[key].get(out.[:mem:])); + } + } + } + }; + + return SUCCESS; +} + +#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION + } // namespace ondemand } // namespace haswell } // namespace simdjson @@ -63427,6 +70400,7 @@ simdjson_inline simdjson_result simdjson_result parser::iterate(padded_string_view json) & noexcept { - if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; } + if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; } json.remove_utf8_bom(); @@ -63641,7 +70615,7 @@ simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(p #ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON simdjson_warn_unused simdjson_inline simdjson_result parser::iterate_allow_incomplete_json(padded_string_view json) & noexcept { - if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; } + if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; } json.remove_utf8_bom(); @@ -63673,10 +70647,7 @@ simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(s } simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(std::string &json) & noexcept { - if(json.capacity() - json.size() < SIMDJSON_PADDING) { - json.reserve(json.size() + SIMDJSON_PADDING); - } - return iterate(padded_string_view(json)); + return iterate(pad_with_reserve(json)); } simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(const std::string &json) & noexcept { @@ -63698,7 +70669,7 @@ simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(c } simdjson_warn_unused simdjson_inline simdjson_result parser::iterate_raw(padded_string_view json) & noexcept { - if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; } + if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; } json.remove_utf8_bom(); @@ -63713,6 +70684,7 @@ simdjson_warn_unused simdjson_inline simdjson_result parser::iter } inline simdjson_result parser::iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept { + // Warning: no check is done on the buffer padding. We trust the user. if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; } if((len >= 3) && (std::memcmp(buf, "\xEF\xBB\xBF", 3) == 0)) { buf += 3; @@ -63721,16 +70693,24 @@ inline simdjson_result parser::iterate_many(const uint8_t *buf, if(allow_comma_separated && batch_size < len) { batch_size = len; } return document_stream(*this, buf, len, batch_size, allow_comma_separated); } + inline simdjson_result parser::iterate_many(const char *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept { + // Warning: no check is done on the buffer padding. We trust the user. return iterate_many(reinterpret_cast(buf), len, batch_size, allow_comma_separated); } -inline simdjson_result parser::iterate_many(const std::string &s, size_t batch_size, bool allow_comma_separated) noexcept { +inline simdjson_result parser::iterate_many(padded_string_view s, size_t batch_size, bool allow_comma_separated) noexcept { + if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; } return iterate_many(s.data(), s.length(), batch_size, allow_comma_separated); } inline simdjson_result parser::iterate_many(const padded_string &s, size_t batch_size, bool allow_comma_separated) noexcept { - return iterate_many(s.data(), s.length(), batch_size, allow_comma_separated); + return iterate_many(padded_string_view(s), batch_size, allow_comma_separated); +} +inline simdjson_result parser::iterate_many(const std::string &s, size_t batch_size, bool allow_comma_separated) noexcept { + return iterate_many(padded_string_view(s), batch_size, allow_comma_separated); +} +inline simdjson_result parser::iterate_many(std::string &s, size_t batch_size, bool allow_comma_separated) noexcept { + return iterate_many(pad(s), batch_size, allow_comma_separated); } - simdjson_pure simdjson_inline size_t parser::capacity() const noexcept { return _capacity; } @@ -63765,6 +70745,34 @@ simdjson_inline simdjson_warn_unused simdjson_result parser::u return result; } +simdjson_inline simdjson_warn_unused ondemand::parser& parser::get_parser() { + return *parser::get_parser_instance(); +} + +simdjson_inline bool release_parser() { + auto &parser_instance = parser::get_threadlocal_parser_if_exists(); + if (parser_instance) { + parser_instance.reset(); + return true; + } + return false; +} + +simdjson_inline simdjson_warn_unused std::unique_ptr& parser::get_parser_instance() { + std::unique_ptr& parser_instance = get_threadlocal_parser_if_exists(); + if (!parser_instance) { + parser_instance.reset(new ondemand::parser()); + } + return parser_instance; +} + +simdjson_inline simdjson_warn_unused std::unique_ptr& parser::get_threadlocal_parser_if_exists() { + // @the-moisrex points out that this could be implemented with std::optional (C++17). + thread_local std::unique_ptr parser_instance = nullptr; + return parser_instance; +} + + } // namespace ondemand } // namespace haswell } // namespace simdjson @@ -63799,8 +70807,13 @@ namespace ondemand { simdjson_inline raw_json_string::raw_json_string(const uint8_t * _buf) noexcept : buf{_buf} {} -simdjson_inline const char * raw_json_string::raw() const noexcept { return reinterpret_cast(buf); } +simdjson_inline const char * raw_json_string::raw() const noexcept { + return reinterpret_cast(buf); +} +simdjson_inline char raw_json_string::operator[](size_t i) const noexcept { + return reinterpret_cast(buf)[i]; +} simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(std::string_view target) noexcept { size_t pos{0}; @@ -63977,6 +70990,10 @@ simdjson_inline simdjson_result simdjson_result::operator[](size_t i) const noexcept { + if (error()) { return error(); } + return first[i]; +} simdjson_inline simdjson_warn_unused simdjson_result simdjson_result::unescape(haswell::ondemand::json_iterator &iter, bool allow_replacement) const noexcept { if (error()) { return error(); } return first.unescape(iter, allow_replacement); @@ -64002,6 +71019,9 @@ simdjson_inline simdjson_warn_unused simdjson_result simdjson_ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/serialization.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */ +/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_builder.h" */ +/* amalgamation skipped (editor-only): #endif */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { @@ -64367,7 +71387,7 @@ simdjson_warn_unused simdjson_inline simdjson_result value_iterator::start if (*_json_iter->peek() == '}') { logger::log_value(*_json_iter, "empty object"); _json_iter->return_current_and_advance(); - end_container(); + SIMDJSON_TRY(end_container()); return false; } return true; @@ -65221,7 +72241,7 @@ simdjson_inline void value_iterator::advance_scalar(const char *type) noexcept { _json_iter->ascend_to(depth()-1); } -simdjson_inline error_code value_iterator::start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept { +simdjson_warn_unused simdjson_inline error_code value_iterator::start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept { logger::log_start_value(*_json_iter, start_position(), depth(), type); // If we're not at the position anymore, we don't want to advance the cursor. const uint8_t *json; @@ -65383,7 +72403,7 @@ simdjson_inline simdjson_result value_iterator::type() const noexcept case '5': case '6': case '7': case '8': case '9': return json_type::number; default: - return TAPE_ERROR; + return json_type::unknown; } } @@ -65423,6 +72443,1097 @@ simdjson_inline simdjson_result::simdjson_res #endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H /* end file simdjson/generic/ondemand/value_iterator-inl.h for haswell */ +// JSON builder inline definitions +/* including simdjson/generic/ondemand/json_string_builder-inl.h for haswell: #include "simdjson/generic/ondemand/json_string_builder-inl.h" */ +/* begin file simdjson/generic/ondemand/json_string_builder-inl.h for haswell */ +/** + * This file is part of the builder API. It is temporarily in the ondemand + * directory but we will move it to a builder directory later. + */ +#include +#include +#include +#ifndef SIMDJSON_GENERIC_STRING_BUILDER_INL_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_INL_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_string_builder.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +/* + * Empirically, we have found that an inlined optimization is important for + * performance. The following macros are not ideal. We should find a better + * way to inline the code. + */ + +#if defined(__SSE2__) || defined(__x86_64__) || defined(__x86_64) || \ + (defined(_M_AMD64) || defined(_M_X64) || \ + (defined(_M_IX86_FP) && _M_IX86_FP == 2)) +#ifndef SIMDJSON_EXPERIMENTAL_HAS_SSE2 +#define SIMDJSON_EXPERIMENTAL_HAS_SSE2 1 +#endif +#endif + +#if defined(__aarch64__) || defined(_M_ARM64) +#ifndef SIMDJSON_EXPERIMENTAL_HAS_NEON +#define SIMDJSON_EXPERIMENTAL_HAS_NEON 1 +#endif +#endif +#if SIMDJSON_EXPERIMENTAL_HAS_NEON +#include +#endif +#if SIMDJSON_EXPERIMENTAL_HAS_SSE2 +#include +#endif + +namespace simdjson { +namespace haswell { +namespace builder { + +static SIMDJSON_CONSTEXPR_LAMBDA std::array + json_quotable_character = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 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, 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, 1, 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, 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, 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, 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, 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, 0, 0, 0}; + +/** + +A possible SWAR implementation of has_json_escapable_byte. It is not used +because it is slower than the current implementation. It is kept here for +reference (to show that we tried it). + +inline bool has_json_escapable_byte(uint64_t x) { + uint64_t is_ascii = 0x8080808080808080ULL & ~x; + uint64_t xor2 = x ^ 0x0202020202020202ULL; + uint64_t lt32_or_eq34 = xor2 - 0x2121212121212121ULL; + uint64_t sub92 = x ^ 0x5C5C5C5C5C5C5C5CULL; + uint64_t eq92 = (sub92 - 0x0101010101010101ULL); + return ((lt32_or_eq34 | eq92) & is_ascii) != 0; +} + +**/ + +SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline bool +simple_needs_escaping(std::string_view v) { + for (char c : v) { + // a table lookup is faster than a series of comparisons + if (json_quotable_character[static_cast(c)]) { + return true; + } + } + return false; +} + +#if SIMDJSON_EXPERIMENTAL_HAS_NEON +simdjson_inline bool fast_needs_escaping(std::string_view view) { + if (view.size() < 16) { + return simple_needs_escaping(view); + } + size_t i = 0; + uint8x16_t running = vdupq_n_u8(0); + uint8x16_t v34 = vdupq_n_u8(34); + uint8x16_t v92 = vdupq_n_u8(92); + + for (; i + 15 < view.size(); i += 16) { + uint8x16_t word = vld1q_u8((const uint8_t *)view.data() + i); + running = vorrq_u8(running, vceqq_u8(word, v34)); + running = vorrq_u8(running, vceqq_u8(word, v92)); + running = vorrq_u8(running, vcltq_u8(word, vdupq_n_u8(32))); + } + if (i < view.size()) { + uint8x16_t word = + vld1q_u8((const uint8_t *)view.data() + view.length() - 16); + running = vorrq_u8(running, vceqq_u8(word, v34)); + running = vorrq_u8(running, vceqq_u8(word, v92)); + running = vorrq_u8(running, vcltq_u8(word, vdupq_n_u8(32))); + } + return vmaxvq_u32(vreinterpretq_u32_u8(running)) != 0; +} +#elif SIMDJSON_EXPERIMENTAL_HAS_SSE2 +simdjson_inline bool fast_needs_escaping(std::string_view view) { + if (view.size() < 16) { + return simple_needs_escaping(view); + } + size_t i = 0; + __m128i running = _mm_setzero_si128(); + for (; i + 15 < view.size(); i += 16) { + + __m128i word = + _mm_loadu_si128(reinterpret_cast(view.data() + i)); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(34))); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(92))); + running = _mm_or_si128( + running, _mm_cmpeq_epi8(_mm_subs_epu8(word, _mm_set1_epi8(31)), + _mm_setzero_si128())); + } + if (i < view.size()) { + __m128i word = _mm_loadu_si128( + reinterpret_cast(view.data() + view.length() - 16)); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(34))); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(92))); + running = _mm_or_si128( + running, _mm_cmpeq_epi8(_mm_subs_epu8(word, _mm_set1_epi8(31)), + _mm_setzero_si128())); + } + return _mm_movemask_epi8(running) != 0; +} +#else +simdjson_inline bool fast_needs_escaping(std::string_view view) { + return simple_needs_escaping(view); +} +#endif + +SIMDJSON_CONSTEXPR_LAMBDA inline size_t +find_next_json_quotable_character(const std::string_view view, + size_t location) noexcept { + + for (auto pos = view.begin() + location; pos != view.end(); ++pos) { + if (json_quotable_character[static_cast(*pos)]) { + return pos - view.begin(); + } + } + return size_t(view.size()); +} + +SIMDJSON_CONSTEXPR_LAMBDA static std::string_view control_chars[] = { + "\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", "\\u0006", + "\\u0007", "\\b", "\\t", "\\n", "\\u000b", "\\f", "\\r", + "\\u000e", "\\u000f", "\\u0010", "\\u0011", "\\u0012", "\\u0013", "\\u0014", + "\\u0015", "\\u0016", "\\u0017", "\\u0018", "\\u0019", "\\u001a", "\\u001b", + "\\u001c", "\\u001d", "\\u001e", "\\u001f"}; + +// All Unicode characters may be placed within the quotation marks, except for +// the characters that MUST be escaped: quotation mark, reverse solidus, and the +// control characters (U+0000 through U+001F). There are two-character sequence +// escape representations of some popular characters: +// \", \\, \b, \f, \n, \r, \t. +SIMDJSON_CONSTEXPR_LAMBDA void escape_json_char(char c, char *&out) { + if (c == '"') { + memcpy(out, "\\\"", 2); + out += 2; + } else if (c == '\\') { + memcpy(out, "\\\\", 2); + out += 2; + } else { + std::string_view v = control_chars[uint8_t(c)]; + memcpy(out, v.data(), v.size()); + out += v.size(); + } +} + +inline size_t write_string_escaped(const std::string_view input, char *out) { + size_t mysize = input.size(); + if (!fast_needs_escaping(input)) { // fast path! + memcpy(out, input.data(), input.size()); + return input.size(); + } + const char *const initout = out; + size_t location = find_next_json_quotable_character(input, 0); + memcpy(out, input.data(), location); + out += location; + escape_json_char(input[location], out); + location += 1; + while (location < mysize) { + size_t newlocation = find_next_json_quotable_character(input, location); + memcpy(out, input.data() + location, newlocation - location); + out += newlocation - location; + location = newlocation; + if (location == mysize) { + break; + } + escape_json_char(input[location], out); + location += 1; + } + return out - initout; +} + +simdjson_inline string_builder::string_builder(size_t initial_capacity) + : buffer(new(std::nothrow) char[initial_capacity]), position(0), + capacity(buffer.get() != nullptr ? initial_capacity : 0), + is_valid(buffer.get() != nullptr) {} + +simdjson_inline bool string_builder::capacity_check(size_t upcoming_bytes) { + // We use the convention that when is_valid is false, then the capacity and + // the position are 0. + // Most of the time, this function will return true. + if (simdjson_likely(upcoming_bytes <= capacity - position)) { + return true; + } + // check for overflow, most of the time there is no overflow + if (simdjson_likely(position + upcoming_bytes < position)) { + return false; + } + // We will rarely get here. + grow_buffer((std::max)(capacity * 2, position + upcoming_bytes)); + // If the buffer allocation failed, we set is_valid to false. + return is_valid; +} + +simdjson_inline void string_builder::grow_buffer(size_t desired_capacity) { + if (!is_valid) { + return; + } + std::unique_ptr new_buffer(new (std::nothrow) char[desired_capacity]); + if (new_buffer.get() == nullptr) { + set_valid(false); + return; + } + std::memcpy(new_buffer.get(), buffer.get(), position); + buffer.swap(new_buffer); + capacity = desired_capacity; +} + +simdjson_inline void string_builder::set_valid(bool valid) noexcept { + if (!valid) { + is_valid = false; + capacity = 0; + position = 0; + buffer.reset(); + } else { + is_valid = true; + } +} + +simdjson_inline size_t string_builder::size() const noexcept { + return position; +} + +simdjson_inline void string_builder::append(char c) noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = c; + } +} + +simdjson_inline void string_builder::append_null() noexcept { + constexpr char null_literal[] = "null"; + constexpr size_t null_len = sizeof(null_literal) - 1; + if (capacity_check(null_len)) { + std::memcpy(buffer.get() + position, null_literal, null_len); + position += null_len; + } +} + +simdjson_inline void string_builder::clear() noexcept { + position = 0; + // if it was invalid, we should try to repair it + if (!is_valid) { + capacity = 0; + buffer.reset(); + is_valid = true; + } +} + +namespace internal { + +template ::value>::type> +simdjson_really_inline int int_log2(number_type x) { + return 63 - leading_zeroes(uint64_t(x) | 1); +} + +simdjson_really_inline int fast_digit_count_32(uint32_t x) { + static uint64_t table[] = { + 4294967296, 8589934582, 8589934582, 8589934582, 12884901788, + 12884901788, 12884901788, 17179868184, 17179868184, 17179868184, + 21474826480, 21474826480, 21474826480, 21474826480, 25769703776, + 25769703776, 25769703776, 30063771072, 30063771072, 30063771072, + 34349738368, 34349738368, 34349738368, 34349738368, 38554705664, + 38554705664, 38554705664, 41949672960, 41949672960, 41949672960, + 42949672960, 42949672960}; + return uint32_t((x + table[int_log2(x)]) >> 32); +} + +simdjson_really_inline int fast_digit_count_64(uint64_t x) { + static uint64_t table[] = {9, + 99, + 999, + 9999, + 99999, + 999999, + 9999999, + 99999999, + 999999999, + 9999999999, + 99999999999, + 999999999999, + 9999999999999, + 99999999999999, + 999999999999999ULL, + 9999999999999999ULL, + 99999999999999999ULL, + 999999999999999999ULL, + 9999999999999999999ULL}; + int y = (19 * int_log2(x) >> 6); + y += x > table[y]; + return y + 1; +} + +template ::value>::type> +simdjson_really_inline size_t digit_count(number_type v) noexcept { + static_assert(sizeof(number_type) == 8 || sizeof(number_type) == 4 || + sizeof(number_type) == 2 || sizeof(number_type) == 1, + "We only support 8-bit, 16-bit, 32-bit and 64-bit numbers"); + SIMDJSON_IF_CONSTEXPR(sizeof(number_type) <= 4) { + return fast_digit_count_32(static_cast(v)); + } + else { + return fast_digit_count_64(static_cast(v)); + } +} +static const char decimal_table[200] = { + 0x30, 0x30, 0x30, 0x31, 0x30, 0x32, 0x30, 0x33, 0x30, 0x34, 0x30, 0x35, + 0x30, 0x36, 0x30, 0x37, 0x30, 0x38, 0x30, 0x39, 0x31, 0x30, 0x31, 0x31, + 0x31, 0x32, 0x31, 0x33, 0x31, 0x34, 0x31, 0x35, 0x31, 0x36, 0x31, 0x37, + 0x31, 0x38, 0x31, 0x39, 0x32, 0x30, 0x32, 0x31, 0x32, 0x32, 0x32, 0x33, + 0x32, 0x34, 0x32, 0x35, 0x32, 0x36, 0x32, 0x37, 0x32, 0x38, 0x32, 0x39, + 0x33, 0x30, 0x33, 0x31, 0x33, 0x32, 0x33, 0x33, 0x33, 0x34, 0x33, 0x35, + 0x33, 0x36, 0x33, 0x37, 0x33, 0x38, 0x33, 0x39, 0x34, 0x30, 0x34, 0x31, + 0x34, 0x32, 0x34, 0x33, 0x34, 0x34, 0x34, 0x35, 0x34, 0x36, 0x34, 0x37, + 0x34, 0x38, 0x34, 0x39, 0x35, 0x30, 0x35, 0x31, 0x35, 0x32, 0x35, 0x33, + 0x35, 0x34, 0x35, 0x35, 0x35, 0x36, 0x35, 0x37, 0x35, 0x38, 0x35, 0x39, + 0x36, 0x30, 0x36, 0x31, 0x36, 0x32, 0x36, 0x33, 0x36, 0x34, 0x36, 0x35, + 0x36, 0x36, 0x36, 0x37, 0x36, 0x38, 0x36, 0x39, 0x37, 0x30, 0x37, 0x31, + 0x37, 0x32, 0x37, 0x33, 0x37, 0x34, 0x37, 0x35, 0x37, 0x36, 0x37, 0x37, + 0x37, 0x38, 0x37, 0x39, 0x38, 0x30, 0x38, 0x31, 0x38, 0x32, 0x38, 0x33, + 0x38, 0x34, 0x38, 0x35, 0x38, 0x36, 0x38, 0x37, 0x38, 0x38, 0x38, 0x39, + 0x39, 0x30, 0x39, 0x31, 0x39, 0x32, 0x39, 0x33, 0x39, 0x34, 0x39, 0x35, + 0x39, 0x36, 0x39, 0x37, 0x39, 0x38, 0x39, 0x39, +}; +} // namespace internal + +template +simdjson_inline void string_builder::append(number_type v) noexcept { + static_assert(std::is_same::value || + std::is_integral::value || + std::is_floating_point::value, + "Unsupported number type"); + // If C++17 is available, we can 'if constexpr' here. + SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + if (v) { + constexpr char true_literal[] = "true"; + constexpr size_t true_len = sizeof(true_literal) - 1; + if (capacity_check(true_len)) { + std::memcpy(buffer.get() + position, true_literal, true_len); + position += true_len; + } + } else { + constexpr char false_literal[] = "false"; + constexpr size_t false_len = sizeof(false_literal) - 1; + if (capacity_check(false_len)) { + std::memcpy(buffer.get() + position, false_literal, false_len); + position += false_len; + } + } + } + else SIMDJSON_IF_CONSTEXPR(std::is_unsigned::value) { + constexpr size_t max_number_size = 20; + if (capacity_check(max_number_size)) { + using unsigned_type = typename std::make_unsigned::type; + unsigned_type pv = static_cast(v); + size_t dc = internal::digit_count(pv); + char *write_pointer = buffer.get() + position + dc - 1; + while (pv >= 100) { + memcpy(write_pointer - 1, &internal::decimal_table[(pv % 100) * 2], 2); + write_pointer -= 2; + pv /= 100; + } + if (pv >= 10) { + *write_pointer-- = char('0' + (pv % 10)); + pv /= 10; + } + *write_pointer = char('0' + pv); + position += dc; + } + } + else SIMDJSON_IF_CONSTEXPR(std::is_integral::value) { + constexpr size_t max_number_size = 20; + if (capacity_check(max_number_size)) { + using unsigned_type = typename std::make_unsigned::type; + bool negative = v < 0; + unsigned_type pv = static_cast(v); + if (negative) { + pv = 0 - pv; // the 0 is for Microsoft + } + size_t dc = internal::digit_count(pv); + if (negative) { + buffer.get()[position++] = '-'; + } + char *write_pointer = buffer.get() + position + dc - 1; + while (pv >= 100) { + memcpy(write_pointer - 1, &internal::decimal_table[(pv % 100) * 2], 2); + write_pointer -= 2; + pv /= 100; + } + if (pv >= 10) { + *write_pointer-- = char('0' + (pv % 10)); + pv /= 10; + } + *write_pointer = char('0' + pv); + position += dc; + } + } + else SIMDJSON_IF_CONSTEXPR(std::is_floating_point::value) { + constexpr size_t max_number_size = 24; + if (capacity_check(max_number_size)) { + // We could specialize for float. + char *end = simdjson::internal::to_chars(buffer.get() + position, nullptr, + double(v)); + position = end - buffer.get(); + } + } +} + +simdjson_inline void +string_builder::escape_and_append(std::string_view input) noexcept { + // escaping might turn a control character into \x00xx so 6 characters. + if (capacity_check(6 * input.size())) { + position += write_string_escaped(input, buffer.get() + position); + } +} + +simdjson_inline void +string_builder::escape_and_append_with_quotes(std::string_view input) noexcept { + // escaping might turn a control character into \x00xx so 6 characters. + if (capacity_check(2 + 6 * input.size())) { + buffer.get()[position++] = '"'; + position += write_string_escaped(input, buffer.get() + position); + buffer.get()[position++] = '"'; + } +} + +simdjson_inline void +string_builder::escape_and_append_with_quotes(char input) noexcept { + // escaping might turn a control character into \x00xx so 6 characters. + if (capacity_check(2 + 6 * 1)) { + buffer.get()[position++] = '"'; + std::string_view cinput(&input, 1); + position += write_string_escaped(cinput, buffer.get() + position); + buffer.get()[position++] = '"'; + } +} + +simdjson_inline void +string_builder::escape_and_append_with_quotes(const char *input) noexcept { + std::string_view cinput(input); + escape_and_append_with_quotes(cinput); +} +#if SIMDJSON_SUPPORTS_CONCEPTS +template +simdjson_inline void string_builder::escape_and_append_with_quotes() noexcept { + escape_and_append_with_quotes(constevalutil::string_constant::value); +} +#endif + +simdjson_inline void string_builder::append_raw(const char *c) noexcept { + size_t len = std::strlen(c); + append_raw(c, len); +} + +simdjson_inline void +string_builder::append_raw(std::string_view input) noexcept { + if (capacity_check(input.size())) { + std::memcpy(buffer.get() + position, input.data(), input.size()); + position += input.size(); + } +} + +simdjson_inline void string_builder::append_raw(const char *str, + size_t len) noexcept { + if (capacity_check(len)) { + std::memcpy(buffer.get() + position, str, len); + position += len; + } +} +#if SIMDJSON_SUPPORTS_CONCEPTS +// Support for optional types (std::optional, etc.) +template + requires(!require_custom_serialization) +simdjson_inline void string_builder::append(const T &opt) { + if (opt) { + append(*opt); + } else { + append_null(); + } +} + +template + requires(require_custom_serialization) +simdjson_inline void string_builder::append(const T &val) { + serialize(*this, val); +} + +template + requires(std::is_convertible::value || + std::is_same::value) +simdjson_inline void string_builder::append(const T &value) { + escape_and_append_with_quotes(value); +} +#endif + +#if SIMDJSON_SUPPORTS_RANGES && SIMDJSON_SUPPORTS_CONCEPTS +// Support for range-based appending (std::ranges::view, etc.) +template + requires(!std::is_convertible::value) +simdjson_inline void string_builder::append(const R &range) noexcept { + auto it = std::ranges::begin(range); + auto end = std::ranges::end(range); + if constexpr (concepts::is_pair) { + start_object(); + + if (it == end) { + end_object(); + return; // Handle empty range + } + // Append first item without leading comma + append_key_value(it->first, it->second); + ++it; + + // Append remaining items with preceding commas + for (; it != end; ++it) { + append_comma(); + append_key_value(it->first, it->second); + } + end_object(); + } else { + start_array(); + if (it == end) { + end_array(); + return; // Handle empty range + } + + // Append first item without leading comma + append(*it); + ++it; + + // Append remaining items with preceding commas + for (; it != end; ++it) { + append_comma(); + append(*it); + } + end_array(); + } +} + +#endif + +#if SIMDJSON_EXCEPTIONS +simdjson_inline string_builder::operator std::string() const noexcept(false) { + return std::string(operator std::string_view()); +} + +simdjson_inline string_builder::operator std::string_view() const + noexcept(false) simdjson_lifetime_bound { + return view(); +} +#endif + +simdjson_inline simdjson_result +string_builder::view() const noexcept { + if (!is_valid) { + return simdjson::OUT_OF_CAPACITY; + } + return std::string_view(buffer.get(), position); +} + +simdjson_inline simdjson_result string_builder::c_str() noexcept { + if (capacity_check(1)) { + buffer.get()[position] = '\0'; + return buffer.get(); + } + return simdjson::OUT_OF_CAPACITY; +} + +simdjson_inline bool string_builder::validate_unicode() const noexcept { + return simdjson::validate_utf8(buffer.get(), position); +} + +simdjson_inline void string_builder::start_object() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = '{'; + } +} + +simdjson_inline void string_builder::end_object() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = '}'; + } +} + +simdjson_inline void string_builder::start_array() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = '['; + } +} + +simdjson_inline void string_builder::end_array() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = ']'; + } +} + +simdjson_inline void string_builder::append_comma() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = ','; + } +} + +simdjson_inline void string_builder::append_colon() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = ':'; + } +} + +template +simdjson_inline void +string_builder::append_key_value(key_type key, value_type value) noexcept { + static_assert(std::is_same::value || + std::is_convertible::value, + "Unsupported key type"); + escape_and_append_with_quotes(key); + append_colon(); + SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + append_null(); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR( + std::is_convertible::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else { + append(value); + } +} + +#if SIMDJSON_SUPPORTS_CONCEPTS +template +simdjson_inline void +string_builder::append_key_value(value_type value) noexcept { + escape_and_append_with_quotes(); + append_colon(); + SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + append_null(); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR( + std::is_convertible::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else { + append(value); + } +} +#endif + +} // namespace builder +} // namespace haswell +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_STRING_BUILDER_INL_H +/* end file simdjson/generic/ondemand/json_string_builder-inl.h for haswell */ +/* including simdjson/generic/ondemand/json_builder.h for haswell: #include "simdjson/generic/ondemand/json_builder.h" */ +/* begin file simdjson/generic/ondemand/json_builder.h for haswell */ +/** + * This file is part of the builder API. It is temporarily in the ondemand directory + * but we will move it to a builder directory later. + */ +#ifndef SIMDJSON_GENERIC_BUILDER_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_string_builder.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/concepts.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ +#if SIMDJSON_STATIC_REFLECTION + +#include +#include +#include +#include +#include +#include +#include +#include +// #include // for std::define_static_string - header not available yet + +namespace simdjson { +namespace haswell { +namespace builder { + +template + requires(concepts::container_but_not_string && !require_custom_serialization) +constexpr void atom(string_builder &b, const T &t) { + auto it = t.begin(); + auto end = t.end(); + if (it == end) { + b.append_raw("[]"); + return; + } + b.append('['); + atom(b, *it); + ++it; + for (; it != end; ++it) { + b.append(','); + atom(b, *it); + } + b.append(']'); +} + +template + requires(std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v) +constexpr void atom(string_builder &b, const T &t) { + b.escape_and_append_with_quotes(t); +} + +template + requires(!require_custom_serialization) +constexpr void atom(string_builder &b, const T &m) { + if (m.empty()) { + b.append_raw("{}"); + return; + } + b.append('{'); + bool first = true; + for (const auto& [key, value] : m) { + if (!first) { + b.append(','); + } + first = false; + // Keys must be convertible to string_view per the concept + b.escape_and_append_with_quotes(key); + b.append(':'); + atom(b, value); + } + b.append('}'); +} + + +template::value && !std::is_same_v>::type> +constexpr void atom(string_builder &b, const number_type t) { + b.append(t); +} + +template + requires(std::is_class_v && !concepts::container_but_not_string && + !concepts::string_view_keyed_map && + !concepts::optional_type && + !concepts::smart_pointer && + !concepts::appendable_containers && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && !require_custom_serialization) +constexpr void atom(string_builder &b, const T &t) { + int i = 0; + b.append('{'); + template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) { + if (i != 0) + b.append(','); + constexpr auto key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(dm))); + b.append_raw(key); + b.append(':'); + atom(b, t.[:dm:]); + i++; + }; + b.append('}'); +} + +// Support for optional types (std::optional, etc.) +template + requires(!require_custom_serialization) +constexpr void atom(string_builder &b, const T &opt) { + if (opt) { + atom(b, opt.value()); + } else { + b.append_raw("null"); + } +} + +// Support for smart pointers (std::unique_ptr, std::shared_ptr, etc.) +template + requires(!require_custom_serialization) +constexpr void atom(string_builder &b, const T &ptr) { + if (ptr) { + atom(b, *ptr); + } else { + b.append_raw("null"); + } +} + +// Support for enums - serialize as string representation using expand approach from P2996R12 +template + requires(std::is_enum_v && !require_custom_serialization) +void atom(string_builder &b, const T &e) { +#if SIMDJSON_STATIC_REFLECTION + constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T)); + template for (constexpr auto enum_val : enumerators) { + constexpr auto enum_str = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(enum_val))); + if (e == [:enum_val:]) { + b.append_raw(enum_str); + return; + } + }; + // Fallback to integer if enum value not found + atom(b, static_cast>(e)); +#else + // Fallback: serialize as integer if reflection not available + atom(b, static_cast>(e)); +#endif +} + +// Support for appendable containers that don't have operator[] (sets, etc.) +template + requires(!concepts::container_but_not_string && !concepts::string_view_keyed_map && + !concepts::optional_type && !concepts::smart_pointer && + !std::is_same_v && + !std::is_same_v && !std::is_same_v && !require_custom_serialization) +constexpr void atom(string_builder &b, const T &container) { + if (container.empty()) { + b.append_raw("[]"); + return; + } + b.append('['); + bool first = true; + for (const auto& item : container) { + if (!first) { + b.append(','); + } + first = false; + atom(b, item); + } + b.append(']'); +} + +// append functions that delegate to atom functions for primitive types +template + requires(std::is_arithmetic_v && !std::is_same_v) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!concepts::container_but_not_string && !concepts::string_view_keyed_map && + !concepts::optional_type && !concepts::smart_pointer && + !std::is_same_v && + !std::is_same_v && !std::is_same_v && !require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +// works for struct +template + requires(std::is_class_v && !concepts::container_but_not_string && + !concepts::string_view_keyed_map && + !concepts::optional_type && + !concepts::smart_pointer && + !concepts::appendable_containers && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && !require_custom_serialization) +void append(string_builder &b, const Z &z) { + int i = 0; + b.append('{'); + template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^Z, std::meta::access_context::unchecked()))) { + if (i != 0) + b.append(','); + constexpr auto key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(dm))); + b.append_raw(key); + b.append(':'); + atom(b, z.[:dm:]); + i++; + }; + b.append('}'); +} + +// works for container that have begin() and end() iterators +template + requires(concepts::container_but_not_string && !require_custom_serialization) +void append(string_builder &b, const Z &z) { + auto it = z.begin(); + auto end = z.end(); + if (it == end) { + b.append_raw("[]"); + return; + } + b.append('['); + atom(b, *it); + ++it; + for (; it != end; ++it) { + b.append(','); + atom(b, *it); + } + b.append(']'); +} + +template + requires (require_custom_serialization) +void append(string_builder &b, const Z &z) { + b.append(z); +} + + +template +simdjson_warn_unused simdjson_result to_json_string(const Z &z, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) { + string_builder b(initial_capacity); + append(b, z); + std::string_view s; + if(auto e = b.view().get(s); e) { return e; } + return std::string(s); +} + +template +simdjson_warn_unused simdjson_error to_json(const Z &z, std::string &s, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) { + string_builder b(initial_capacity); + append(b, z); + std::string_view view; + if(auto e = b.view().get(view); e) { return e; } + s.assign(view); + return SUCCESS; +} + +template +string_builder& operator<<(string_builder& b, const Z& z) { + append(b, z); + return b; +} + +// extract_from: Serialize only specific fields from a struct to JSON +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +void extract_from(string_builder &b, const T &obj) { + // Helper to check if a field name matches any of the requested fields + auto should_extract = [](std::string_view field_name) constexpr -> bool { + return ((FieldNames.view() == field_name) || ...); + }; + + b.append('{'); + bool first = true; + + // Iterate through all members of T using reflection + template for (constexpr auto mem : std::define_static_array( + std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) { + + if constexpr (std::meta::is_public(mem)) { + constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem)); + + // Only serialize this field if it's in our list of requested fields + if constexpr (should_extract(key)) { + if (!first) { + b.append(','); + } + first = false; + + // Serialize the key + constexpr auto quoted_key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(mem))); + b.append_raw(quoted_key); + b.append(':'); + + // Serialize the value + atom(b, obj.[:mem:]); + } + } + }; + + b.append('}'); +} + +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_result extract_from(const T &obj, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) { + string_builder b(initial_capacity); + extract_from(b, obj); + std::string_view s; + if(auto e = b.view().get(s); e) { return e; } + return std::string(s); +} + +} // namespace builder +} // namespace haswell +// Alias the function template to 'to' in the global namespace +template +simdjson_warn_unused simdjson_result to_json(const Z &z, size_t initial_capacity = haswell::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { + haswell::builder::string_builder b(initial_capacity); + haswell::builder::append(b, z); + std::string_view s; + if(auto e = b.view().get(s); e) { return e; } + return std::string(s); +} +template +simdjson_warn_unused simdjson_error to_json(const Z &z, std::string &s, size_t initial_capacity = haswell::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { + haswell::builder::string_builder b(initial_capacity); + haswell::builder::append(b, z); + std::string_view view; + if(auto e = b.view().get(view); e) { return e; } + s.assign(view); + return SUCCESS; +} +// Global namespace function for extract_from +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_result extract_from(const T &obj, size_t initial_capacity = haswell::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { + haswell::builder::string_builder b(initial_capacity); + haswell::builder::extract_from(b, obj); + std::string_view s; + if(auto e = b.view().get(s); e) { return e; } + return std::string(s); +} + +} // namespace simdjson + +#endif // SIMDJSON_STATIC_REFLECTION + +#endif +/* end file simdjson/generic/ondemand/json_builder.h for haswell */ /* end file simdjson/generic/ondemand/amalgamated.h for haswell */ /* including simdjson/haswell/end.h: #include "simdjson/haswell/end.h" */ @@ -65729,7 +73840,6 @@ namespace simd { friend simdjson_really_inline uint64_t operator==(const simd8 lhs, const simd8 rhs) { return _mm512_cmpeq_epi8_mask(lhs, rhs); } - static const int SIZE = sizeof(base::value); template @@ -66048,7 +74158,7 @@ using namespace simd; struct backslash_and_quote { public: static constexpr uint32_t BYTES_PROCESSED = 64; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); + simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; } simdjson_inline bool has_backslash() { return ((quote_bits - 1) & bs_bits) != 0; } @@ -66072,6 +74182,35 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin }; } + + +struct escaping { + static constexpr uint32_t BYTES_PROCESSED = 64; + simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst); + + simdjson_inline bool has_escape() { return escape_bits != 0; } + simdjson_inline int escape_index() { return trailing_zeroes(uint64_t(escape_bits)); } + + __mmask64 escape_bits; +}; // struct escaping + + + +simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) { + static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes"); + simd8 v(src); + v.store(dst); + __mmask64 is_quote = _mm512_cmpeq_epi8_mask(v, _mm512_set1_epi8('"')); + __mmask64 is_backslash = _mm512_cmpeq_epi8_mask(v, _mm512_set1_epi8('\\')); + __mmask64 is_control = _mm512_cmplt_epi8_mask(v, _mm512_set1_epi8(32)); + return { + (is_backslash | is_quote | is_control) + }; +} + + + + } // unnamed namespace } // namespace icelake } // namespace simdjson @@ -66198,7 +74337,7 @@ class value_iterator; /* end file simdjson/generic/ondemand/base.h for icelake */ /* including simdjson/generic/ondemand/deserialize.h for icelake: #include "simdjson/generic/ondemand/deserialize.h" */ /* begin file simdjson/generic/ondemand/deserialize.h for icelake */ -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS #ifndef SIMDJSON_ONDEMAND_DESERIALIZE_H /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ @@ -66207,55 +74346,8 @@ class value_iterator; /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ -#include namespace simdjson { -namespace tag_invoke_fn_ns { -void tag_invoke(); - -struct tag_invoke_fn { - template - requires requires(Tag tag, Args &&...args) { - tag_invoke(std::forward(tag), std::forward(args)...); - } - constexpr auto operator()(Tag tag, Args &&...args) const - noexcept(noexcept(tag_invoke(std::forward(tag), - std::forward(args)...))) - -> decltype(tag_invoke(std::forward(tag), - std::forward(args)...)) { - return tag_invoke(std::forward(tag), std::forward(args)...); - } -}; -} // namespace tag_invoke_fn_ns - -inline namespace tag_invoke_ns { -inline constexpr tag_invoke_fn_ns::tag_invoke_fn tag_invoke = {}; -} // namespace tag_invoke_ns - -template -concept tag_invocable = requires(Tag tag, Args... args) { - tag_invoke(std::forward(tag), std::forward(args)...); -}; - -template -concept nothrow_tag_invocable = - tag_invocable && requires(Tag tag, Args... args) { - { - tag_invoke(std::forward(tag), std::forward(args)...) - } noexcept; - }; - -template -using tag_invoke_result = - std::invoke_result; - -template -using tag_invoke_result_t = - std::invoke_result_t; - -template using tag_t = std::decay_t; - - struct deserialize_tag; /// These types are deserializable in a built-in way @@ -66277,7 +74369,7 @@ template concept custom_deserializable = tag_invocable; template -concept deserializable = custom_deserializable || is_builtin_deserializable_v; +concept deserializable = custom_deserializable || is_builtin_deserializable_v || concepts::optional_type; template concept nothrow_custom_deserializable = nothrow_tag_invocable; @@ -66288,28 +74380,44 @@ concept nothrow_deserializable = nothrow_custom_deserializable || is_bu /// Deserialize Tag inline constexpr struct deserialize_tag { + using array_type = icelake::ondemand::array; + using object_type = icelake::ondemand::object; using value_type = icelake::ondemand::value; using document_type = icelake::ondemand::document; using document_reference_type = icelake::ondemand::document_reference; + // Customization Point for array + template + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(array_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for object + template + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(object_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + // Customization Point for value template requires custom_deserializable - [[nodiscard]] constexpr /* error_code */ auto operator()(value_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + simdjson_warn_unused constexpr /* error_code */ auto operator()(value_type &object, T& output) const noexcept(nothrow_custom_deserializable) { return tag_invoke(*this, object, output); } // Customization Point for document template requires custom_deserializable - [[nodiscard]] constexpr /* error_code */ auto operator()(document_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + simdjson_warn_unused constexpr /* error_code */ auto operator()(document_type &object, T& output) const noexcept(nothrow_custom_deserializable) { return tag_invoke(*this, object, output); } // Customization Point for document reference template requires custom_deserializable - [[nodiscard]] constexpr /* error_code */ auto operator()(document_reference_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + simdjson_warn_unused constexpr /* error_code */ auto operator()(document_reference_type &object, T& output) const noexcept(nothrow_custom_deserializable) { return tag_invoke(*this, object, output); } @@ -66319,7 +74427,7 @@ inline constexpr struct deserialize_tag { } // namespace simdjson #endif // SIMDJSON_ONDEMAND_DESERIALIZE_H -#endif // SIMDJSON_SUPPORTS_DESERIALIZATION +#endif // SIMDJSON_SUPPORTS_CONCEPTS /* end file simdjson/generic/ondemand/deserialize.h for icelake */ /* including simdjson/generic/ondemand/value_iterator.h for icelake: #include "simdjson/generic/ondemand/value_iterator.h" */ @@ -66661,7 +74769,7 @@ class value_iterator { simdjson_warn_unused simdjson_inline simdjson_result get_root_number(bool check_trailing) noexcept; simdjson_warn_unused simdjson_inline simdjson_result is_root_null(bool check_trailing) noexcept; - simdjson_inline error_code error() const noexcept; + simdjson_warn_unused simdjson_inline error_code error() const noexcept; simdjson_inline uint8_t *&string_buf_loc() noexcept; simdjson_inline const json_iterator &json_iter() const noexcept; simdjson_inline json_iterator &json_iter() noexcept; @@ -66745,8 +74853,8 @@ class value_iterator { simdjson_inline const uint8_t *peek_non_root_scalar(const char *type) noexcept; - simdjson_inline error_code start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept; - simdjson_inline error_code end_container() noexcept; + simdjson_warn_unused simdjson_inline error_code start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept; + simdjson_warn_unused simdjson_inline error_code end_container() noexcept; /** * Advance to a place expecting a value (increasing depth). @@ -66756,8 +74864,8 @@ class value_iterator { */ simdjson_inline simdjson_result advance_to_value() noexcept; - simdjson_inline error_code incorrect_type_error(const char *message) const noexcept; - simdjson_inline error_code error_unless_more_tokens(uint32_t tokens=1) const noexcept; + simdjson_warn_unused simdjson_inline error_code incorrect_type_error(const char *message) const noexcept; + simdjson_warn_unused simdjson_inline error_code error_unless_more_tokens(uint32_t tokens=1) const noexcept; simdjson_inline bool is_at_start() const noexcept; /** @@ -66794,7 +74902,7 @@ class value_iterator { /** @copydoc error_code json_iterator::end_position() const noexcept; */ simdjson_inline token_position end_position() const noexcept; /** @copydoc error_code json_iterator::report_error(error_code error, const char *message) noexcept; */ - simdjson_inline error_code report_error(error_code error, const char *message) noexcept; + simdjson_warn_unused simdjson_inline error_code report_error(error_code error, const char *message) noexcept; friend class document; friend class object; @@ -66859,13 +74967,14 @@ class value { * * You may use get_double(), get_bool(), get_uint64(), get_int64(), * get_object(), get_array(), get_raw_json_string(), or get_string() instead. + * When SIMDJSON_SUPPORTS_CONCEPTS is set, custom types are also supported. * * @returns A value of the given type, parsed from the JSON. * @returns INCORRECT_TYPE If the JSON value is not the given type. */ template simdjson_inline simdjson_result get() -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept @@ -66882,22 +74991,38 @@ class value { * Get this value as the given type. * * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool + * If the macro SIMDJSON_SUPPORTS_CONCEPTS is set, then custom types are also supported. * * @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized. * @returns INCORRECT_TYPE If the JSON value is not an object. * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. */ template - simdjson_inline error_code get(T &out) -#if SIMDJSON_SUPPORTS_DESERIALIZATION + simdjson_warn_unused simdjson_inline error_code get(T &out) +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept #endif { - #if SIMDJSON_SUPPORTS_DESERIALIZATION + #if SIMDJSON_SUPPORTS_CONCEPTS if constexpr (custom_deserializable) { return deserialize(*this, out); + } else if constexpr (concepts::optional_type) { + using value_type = typename std::remove_cvref_t::value_type; + + // Check if the value is null + bool is_null_value; + SIMDJSON_TRY( is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); // Set to nullopt + return SUCCESS; + } + + if (!out) { + out.emplace(); + } + return get(out.value()); } else { static_assert(!sizeof(T), "The get method with type T is not implemented by the simdjson library. " "And you do not seem to have added support for it. Indeed, we have that " @@ -66907,7 +75032,7 @@ class value { static_cast(out); // to get rid of unused errors return UNINITIALIZED; } -#else // SIMDJSON_SUPPORTS_DESERIALIZATION +#else // SIMDJSON_SUPPORTS_CONCEPTS // Unless the simdjson library or the user provides an inline implementation, calling this method should // immediately fail. static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " @@ -67025,7 +75150,7 @@ class value { * @returns INCORRECT_TYPE if the JSON value is not a string. Otherwise, we return SUCCESS. */ template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; /** * Cast this JSON value to a "wobbly" string. @@ -67546,7 +75671,7 @@ struct simdjson_result : public icelake::implementatio simdjson_inline simdjson_result get_double_in_string() noexcept; simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; simdjson_inline simdjson_result get_wobbly_string() noexcept; simdjson_inline simdjson_result get_raw_json_string() noexcept; simdjson_inline simdjson_result get_bool() noexcept; @@ -67627,7 +75752,22 @@ struct simdjson_result : public icelake::implementatio simdjson_result operator[](int) noexcept = delete; /** - * Get the type of this JSON value. + * Get the type of this JSON value. It does not validate or consume the value. + * E.g., you must still call "is_null()" to check that a value is null even if + * "type()" returns json_type::null. + * + * Given a valid JSON document, the answer can be one of + * simdjson::ondemand::json_type::object, + * simdjson::ondemand::json_type::array, + * simdjson::ondemand::json_type::string, + * simdjson::ondemand::json_type::number, + * simdjson::ondemand::json_type::boolean, + * simdjson::ondemand::json_type::null. + * + * Starting with simdjson 4.0, this function will return simdjson::ondemand::json_type::unknown + * given a bad token. + * This allows you to identify a case such as {"key": NaN} and identify the NaN value. + * The simdjson::ondemand::json_type::unknown value should only happen with non-valid JSON. * * NOTE: If you're only expecting a value to be one type (a typical case), it's generally * better to just call .get_double, .get_string, etc. and check for INCORRECT_TYPE (or just @@ -68121,14 +76261,14 @@ class json_iterator { * @param error The error to report. Must not be SUCCESS, UNINITIALIZED, INCORRECT_TYPE, or NO_SUCH_FIELD. * @param message An error message to report with the error. */ - simdjson_inline error_code report_error(error_code error, const char *message) noexcept; + simdjson_warn_unused simdjson_inline error_code report_error(error_code error, const char *message) noexcept; /** * Log error, but don't stop iteration. * @param error The error to report. Must be INCORRECT_TYPE, or NO_SUCH_FIELD. * @param message An error message to report with the error. */ - simdjson_inline error_code optional_error(error_code error, const char *message) noexcept; + simdjson_warn_unused simdjson_inline error_code optional_error(error_code error, const char *message) noexcept; /** * Take an input in json containing max_len characters and attempt to copy it over to tmpbuf, a buffer with @@ -68148,7 +76288,7 @@ class json_iterator { simdjson_inline void reenter_child(token_position position, depth_t child_depth) noexcept; - simdjson_inline error_code consume_character(char c) noexcept; + simdjson_warn_unused simdjson_inline error_code consume_character(char c) noexcept; #if SIMDJSON_DEVELOPMENT_CHECKS simdjson_inline token_position start_position(depth_t depth) const noexcept; simdjson_inline void set_start_position(depth_t depth, token_position position) noexcept; @@ -68239,6 +76379,7 @@ namespace ondemand { * The type of a JSON value. */ enum class json_type { + unknown=0, // Start at 1 to catch uninitialized / default values more easily array=1, ///< A JSON array ( [ 1, 2, 3 ... ] ) object, ///< A JSON object ( { "a": 1, "b" 2, ... } ) @@ -68445,6 +76586,12 @@ class raw_json_string { */ simdjson_inline const char * raw() const noexcept; + /** + * Get the character at index i. This is unchecked. + * [0] when the string is of length 0 returns the final quote ("). + */ + simdjson_inline char operator[](size_t i) const noexcept; + /** * This compares the current instance to the std::string_view target: returns true if * they are byte-by-byte equal (no escaping is done) on target.size() characters, @@ -68584,10 +76731,10 @@ struct simdjson_result : public icelake::imp simdjson_inline ~simdjson_result() noexcept = default; ///< @private simdjson_inline simdjson_result raw() const noexcept; + simdjson_inline char operator[](size_t) const noexcept; simdjson_inline simdjson_warn_unused simdjson_result unescape(icelake::ondemand::json_iterator &iter, bool allow_replacement) const noexcept; simdjson_inline simdjson_warn_unused simdjson_result unescape_wobbly(icelake::ondemand::json_iterator &iter) const noexcept; }; - } // namespace simdjson #endif // SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_H @@ -68603,6 +76750,7 @@ struct simdjson_result : public icelake::imp /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ #include +#include namespace simdjson { namespace icelake { @@ -68721,7 +76869,9 @@ class parser { simdjson_warn_unused simdjson_result iterate(std::string_view json, size_t capacity) & noexcept; /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ simdjson_warn_unused simdjson_result iterate(const std::string &json) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + /** @overload simdjson_result iterate(padded_string_view json) & noexcept + The string instance might be have its capacity extended. Note that this can still + result in AddressSanitizer: container-overflow in some cases. */ simdjson_warn_unused simdjson_result iterate(std::string &json) & noexcept; /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ simdjson_warn_unused simdjson_result iterate(const simdjson_result &json) & noexcept; @@ -68809,6 +76959,11 @@ class parser { * Setting batch_size to excessively large or excessively small values may impact negatively the * performance. * + * ### Threads + * + * When compiled with SIMDJSON_THREADS_ENABLED, this method will use a single thread under the + * hood to do some lookahead. + * * ### REQUIRED: Buffer Padding * * The buffer must have at least SIMDJSON_PADDING extra allocated bytes. It does not matter what @@ -68816,10 +76971,10 @@ class parser { * using a sanitizer that verifies that no uninitialized byte is read, then you should initialize the * SIMDJSON_PADDING bytes to avoid runtime warnings. * - * ### Threads + * This is checked automatically with all iterate_many function calls, except for the two + * that take pointers (const char* or const uint8_t*). * - * When compiled with SIMDJSON_THREADS_ENABLED, this method will use a single thread under the - * hood to do some lookahead. + * ### Threads * * ### Parser Capacity * @@ -68845,14 +77000,16 @@ class parser { */ inline simdjson_result iterate_many(const uint8_t *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ + inline simdjson_result iterate_many(padded_string_view json, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ inline simdjson_result iterate_many(const char *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ inline simdjson_result iterate_many(const std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; - inline simdjson_result iterate_many(const std::string &&s, size_t batch_size, bool allow_comma_separated = false) = delete;// unsafe + /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) + the string might be automatically padded with up to SIMDJSON_PADDING whitespace characters */ + inline simdjson_result iterate_many(std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ inline simdjson_result iterate_many(const padded_string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; - inline simdjson_result iterate_many(const padded_string &&s, size_t batch_size, bool allow_comma_separated = false) = delete;// unsafe - /** @private We do not want to allow implicit conversion from C string to std::string. */ simdjson_result iterate_many(const char *buf, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept = delete; @@ -68954,13 +77111,39 @@ class parser { bool string_buffer_overflow(const uint8_t *string_buf_loc) const noexcept; #endif + /** + * Get a unique parser instance corresponding to the current thread. + * This instance can be safely used within the current thread, but it should + * not be passed to other threads. + * + * A parser should only be used for one document at a time. + * + * Our simdjson::from functions use this parser instance. + * + * You can free the related parser by calling release_parser(). + */ + static simdjson_inline simdjson_warn_unused ondemand::parser& get_parser(); + /** + * Release the parser instance initialized by get_parser() and all the + * associated resources (memory). Returns true if a parser instance + * was released. + */ + static simdjson_inline bool release_parser(); + private: + friend bool release_parser(); + friend ondemand::parser& get_parser(); + /** Get the thread-local parser instance, allocates it if needed */ + static simdjson_inline simdjson_warn_unused std::unique_ptr& get_parser_instance(); + /** Get the thread-local parser instance, it might be null */ + static simdjson_inline simdjson_warn_unused std::unique_ptr& get_threadlocal_parser_if_exists(); /** @private [for benchmarking access] The implementation to use */ std::unique_ptr implementation{}; size_t _capacity{0}; size_t _max_capacity; size_t _max_depth{DEFAULT_MAX_DEPTH}; std::unique_ptr string_buf{}; + #if SIMDJSON_DEVELOPMENT_CHECKS std::unique_ptr start_positions{}; #endif @@ -68988,6 +77171,315 @@ struct simdjson_result : public icelake::implementati #endif // SIMDJSON_GENERIC_ONDEMAND_PARSER_H /* end file simdjson/generic/ondemand/parser.h for icelake */ +// JSON builder - needed for extract_into functionality +/* including simdjson/generic/ondemand/json_string_builder.h for icelake: #include "simdjson/generic/ondemand/json_string_builder.h" */ +/* begin file simdjson/generic/ondemand/json_string_builder.h for icelake */ +/** + * This file is part of the builder API. It is temporarily in the ondemand directory + * but we will move it to a builder directory later. + */ +#ifndef SIMDJSON_GENERIC_STRING_BUILDER_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { + + +#if SIMDJSON_SUPPORTS_CONCEPTS + +namespace icelake { +namespace builder { + class string_builder; +}} + +template +struct has_custom_serialization : std::false_type {}; + +inline constexpr struct serialize_tag { + template + requires custom_deserializable + constexpr void operator()(icelake::builder::string_builder& b, T& obj) const{ + return tag_invoke(*this, b, obj); + } + + +} serialize{}; +template +struct has_custom_serialization(), std::declval())) +>> : std::true_type {}; + +template +constexpr bool require_custom_serialization = has_custom_serialization::value; +#else +struct has_custom_serialization : std::false_type {}; +#endif // SIMDJSON_SUPPORTS_CONCEPTS + +namespace icelake { +namespace builder { +/** + * A builder for JSON strings representing documents. This is a low-level + * builder that is not meant to be used directly by end-users. Though it + * supports atomic types (Booleans, strings), it does not support composed + * types (arrays and objects). + * + * Ultimately, this class can support kernel-specific optimizations. E.g., + * it may make use of SIMD instructions to escape strings faster. + */ +class string_builder { +public: + simdjson_inline string_builder(size_t initial_capacity = DEFAULT_INITIAL_CAPACITY); + + static constexpr size_t DEFAULT_INITIAL_CAPACITY = 1024; + + /** + * Append number (includes Booleans). Booleans are mapped to the strings + * false and true. Numbers are converted to strings abiding by the JSON standard. + * Floating-point numbers are converted to the shortest string that 'correctly' + * represents the number. + */ + template::value>::type> + simdjson_inline void append(number_type v) noexcept; + + /** + * Append character c. + */ + simdjson_inline void append(char c) noexcept; + + /** + * Append the string 'null'. + */ + simdjson_inline void append_null() noexcept; + + /** + * Clear the content. + */ + simdjson_inline void clear() noexcept; + + /** + * Append the std::string_view, after escaping it. + * There is no UTF-8 validation. + */ + simdjson_inline void escape_and_append(std::string_view input) noexcept; + + /** + * Append the std::string_view surrounded by double quotes, after escaping it. + * There is no UTF-8 validation. + */ + simdjson_inline void escape_and_append_with_quotes(std::string_view input) noexcept; +#if SIMDJSON_SUPPORTS_CONCEPTS + template + simdjson_inline void escape_and_append_with_quotes() noexcept; +#endif + /** + * Append the character surrounded by double quotes, after escaping it. + * There is no UTF-8 validation. + */ + simdjson_inline void escape_and_append_with_quotes(char input) noexcept; + + /** + * Append the character surrounded by double quotes, after escaping it. + * There is no UTF-8 validation. + */ + simdjson_inline void escape_and_append_with_quotes(const char* input) noexcept; + + /** + * Append the C string directly, without escaping. + * There is no UTF-8 validation. + */ + simdjson_inline void append_raw(const char *c) noexcept; + + /** + * Append "{" to the buffer. + */ + simdjson_inline void start_object() noexcept; + + /** + * Append "}" to the buffer. + */ + simdjson_inline void end_object() noexcept; + + /** + * Append "[" to the buffer. + */ + simdjson_inline void start_array() noexcept; + + /** + * Append "]" to the buffer. + */ + simdjson_inline void end_array() noexcept; + + /** + * Append "," to the buffer. + */ + simdjson_inline void append_comma() noexcept; + + /** + * Append ":" to the buffer. + */ + simdjson_inline void append_colon() noexcept; + + /** + * Append a key-value pair to the buffer. + * The key is escaped and surrounded by double quotes. + * The value is escaped if it is a string. + */ + template + simdjson_inline void append_key_value(key_type key, value_type value) noexcept; +#if SIMDJSON_SUPPORTS_CONCEPTS + template + simdjson_inline void append_key_value(value_type value) noexcept; + + // Support for optional types (std::optional, etc.) + template + requires(!require_custom_serialization) + simdjson_inline void append(const T &opt); + + template + requires(require_custom_serialization) + simdjson_inline void append(const T &val); + + // Support for string-like types + template + requires(std::is_convertible::value || + std::is_same::value ) + simdjson_inline void append(const T &value); +#endif +#if SIMDJSON_SUPPORTS_RANGES && SIMDJSON_SUPPORTS_CONCEPTS + // Support for range-based appending (std::ranges::view, etc.) + template +requires (!std::is_convertible::value) + simdjson_inline void append(const R &range) noexcept; +#endif + /** + * Append the std::string_view directly, without escaping. + * There is no UTF-8 validation. + */ + simdjson_inline void append_raw(std::string_view input) noexcept; + + /** + * Append len characters from str. + * There is no UTF-8 validation. + */ + simdjson_inline void append_raw(const char *str, size_t len) noexcept; +#if SIMDJSON_EXCEPTIONS + /** + * Creates an std::string from the written JSON buffer. + * Throws if memory allocation failed + * + * The result may not be valid UTF-8 if some of your content was not valid UTF-8. + * Use validate_unicode() to check the content if needed. + */ + simdjson_inline operator std::string() const noexcept(false); + + /** + * Creates an std::string_view from the written JSON buffer. + * Throws if memory allocation failed. + * + * The result may not be valid UTF-8 if some of your content was not valid UTF-8. + * Use validate_unicode() to check the content if needed. + */ + simdjson_inline operator std::string_view() const noexcept(false) simdjson_lifetime_bound; +#endif + + /** + * Returns a view on the written JSON buffer. Returns an error + * if memory allocation failed. + * + * The result may not be valid UTF-8 if some of your content was not valid UTF-8. + * Use validate_unicode() to check the content. + */ + simdjson_inline simdjson_result view() const noexcept; + + /** + * Appends the null character to the buffer and returns + * a pointer to the beginning of the written JSON buffer. + * Returns an error if memory allocation failed. + * The result is null-terminated. + * + * The result may not be valid UTF-8 if some of your content was not valid UTF-8. + * Use validate_unicode() to check the content. + */ + simdjson_inline simdjson_result c_str() noexcept; + + /** + * Return true if the content is valid UTF-8. + */ + simdjson_inline bool validate_unicode() const noexcept; + + /** + * Returns the current size of the written JSON buffer. + * If an error occurred, returns 0. + */ + simdjson_inline size_t size() const noexcept; + +private: + /** + * Returns true if we can write at least upcoming_bytes bytes. + * The underlying buffer is reallocated if needed. It is designed + * to be called before writing to the buffer. It should be fast. + */ + simdjson_inline bool capacity_check(size_t upcoming_bytes); + + /** + * Grow the buffer to at least desired_capacity bytes. + * If the allocation fails, is_valid is set to false. We expect + * that this function would not be repeatedly called. + */ + simdjson_inline void grow_buffer(size_t desired_capacity); + + /** + * We use this helper function to make sure that is_valid is kept consistent. + */ + simdjson_inline void set_valid(bool valid) noexcept; + + std::unique_ptr buffer{}; + size_t position{0}; + size_t capacity{0}; + bool is_valid{true}; +}; + + + +} +} + + +#if !SIMDJSON_STATIC_REFLECTION +// fallback implementation until we have static reflection +template +simdjson_warn_unused simdjson_result to_json(const Z &z, size_t initial_capacity = simdjson::icelake::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { + simdjson::icelake::builder::string_builder b(initial_capacity); + b.append(z); + std::string_view s; + auto e = b.view().get(s); + if(e) { return e; } + return std::string(s); +} +template +simdjson_warn_unused simdjson_error to_json(const Z &z, std::string &s, size_t initial_capacity = simdjson::icelake::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { + simdjson::icelake::builder::string_builder b(initial_capacity); + b.append(z); + std::string_view sv; + auto e = b.view().get(sv); + if(e) { return e; } + s.assign(sv.data(), sv.size()); + return simdjson::SUCCESS; +} +#endif + +#if SIMDJSON_SUPPORTS_CONCEPTS +#endif // SIMDJSON_SUPPORTS_CONCEPTS + +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_STRING_BUILDER_H +/* end file simdjson/generic/ondemand/json_string_builder.h for icelake */ + // All other declarations /* including simdjson/generic/ondemand/array.h for icelake: #include "simdjson/generic/ondemand/array.h" */ /* begin file simdjson/generic/ondemand/array.h for icelake */ @@ -69124,11 +77616,42 @@ class array { * - INDEX_OUT_OF_BOUNDS if the array index is larger than an array length */ simdjson_inline simdjson_result at(size_t index) noexcept; + +#if SIMDJSON_SUPPORTS_CONCEPTS + /** + * Get this array as the given type. + * + * @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized. + * @returns INCORRECT_TYPE If the JSON array is not of the given type. + * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. + */ + template + simdjson_warn_unused simdjson_inline error_code get(T &out) + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) { + static_assert(custom_deserializable); + return deserialize(*this, out); + } + /** + * Get this array as the given type. + * + * @returns A value of the given type, parsed from the JSON. + * @returns INCORRECT_TYPE If the JSON value is not the given type. + */ + template + simdjson_inline simdjson_result get() + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) + { + static_assert(std::is_default_constructible::value, "The specified type is not default constructible."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } +#endif // SIMDJSON_SUPPORTS_CONCEPTS protected: /** * Go to the end of the array, no matter where you are right now. */ - simdjson_inline error_code consume() noexcept; + simdjson_warn_unused simdjson_inline error_code consume() noexcept; /** * Begin array iteration. @@ -69202,7 +77725,28 @@ struct simdjson_result : public icelake::implementatio simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; simdjson_inline simdjson_result raw_json() noexcept; +#if SIMDJSON_SUPPORTS_CONCEPTS + // TODO: move this code into object-inl.h + template + simdjson_inline simdjson_result get() noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + return first; + } + return first.get(); + } + template + simdjson_warn_unused simdjson_inline error_code get(T& out) noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + out = first; + } else { + SIMDJSON_TRY( first.get(out) ); + } + return SUCCESS; + } +#endif // SIMDJSON_SUPPORTS_CONCEPTS }; } // namespace simdjson @@ -69247,7 +77791,8 @@ class array_iterator { * * Part of the std::iterator interface. */ - simdjson_inline simdjson_result operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. + simdjson_inline simdjson_result + operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. /** * Check if we are at the end of the JSON. * @@ -69271,6 +77816,11 @@ class array_iterator { */ simdjson_inline array_iterator &operator++() noexcept; + /** + * Check if the array is at the end. + */ + simdjson_warn_unused simdjson_inline bool at_end() const noexcept; + private: value_iterator iter{}; @@ -69289,7 +77839,6 @@ namespace simdjson { template<> struct simdjson_result : public icelake::implementation_simdjson_result_base { -public: simdjson_inline simdjson_result(icelake::ondemand::array_iterator &&value) noexcept; ///< @private simdjson_inline simdjson_result(error_code error) noexcept; ///< @private simdjson_inline simdjson_result() noexcept = default; @@ -69302,6 +77851,8 @@ struct simdjson_result : public icelake::impl simdjson_inline bool operator==(const simdjson_result &) const noexcept; simdjson_inline bool operator!=(const simdjson_result &) const noexcept; simdjson_inline simdjson_result &operator++() noexcept; + + simdjson_warn_unused simdjson_inline bool at_end() const noexcept; }; } // namespace simdjson @@ -69429,7 +77980,7 @@ class document { * @returns INCORRECT_TYPE if the JSON value is not a string. Otherwise, we return SUCCESS. */ template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; /** * Cast this JSON value to a string. * @@ -69495,7 +78046,7 @@ class document { */ template simdjson_inline simdjson_result get() & -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept @@ -69518,7 +78069,7 @@ class document { */ template simdjson_inline simdjson_result get() && -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept @@ -69536,18 +78087,18 @@ class document { * Be mindful that the document instance must remain in scope while you are accessing object, array and value instances. * * @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized. - * @returns INCORRECT_TYPE If the JSON value is not an object. + * @returns INCORRECT_TYPE If the JSON value is of the given type. * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. */ template - simdjson_inline error_code get(T &out) & -#if SIMDJSON_SUPPORTS_DESERIALIZATION + simdjson_warn_unused simdjson_inline error_code get(T &out) & +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept #endif { -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS if constexpr (custom_deserializable) { return deserialize(*this, out); } else { @@ -69559,7 +78110,7 @@ class document { static_cast(out); // to get rid of unused errors return UNINITIALIZED; } -#else // SIMDJSON_SUPPORTS_DESERIALIZATION +#else // SIMDJSON_SUPPORTS_CONCEPTS // Unless the simdjson library or the user provides an inline implementation, calling this method should // immediately fail. static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " @@ -69569,7 +78120,7 @@ class document { " You may also add support for custom types, see our documentation."); static_cast(out); // to get rid of unused errors return UNINITIALIZED; -#endif // SIMDJSON_SUPPORTS_DESERIALIZATION +#endif // SIMDJSON_SUPPORTS_CONCEPTS } /** @overload template error_code get(T &out) & noexcept */ @@ -69634,7 +78185,7 @@ class document { * time it parses a document or when it is destroyed. * @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string. */ - simdjson_inline operator std::string_view() noexcept(false); + simdjson_inline operator std::string_view() noexcept(false) simdjson_lifetime_bound; /** * Cast this JSON value to a raw_json_string. * @@ -69643,7 +78194,7 @@ class document { * @returns A pointer to the raw JSON for the given string. * @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string. */ - simdjson_inline operator raw_json_string() noexcept(false); + simdjson_inline operator raw_json_string() noexcept(false) simdjson_lifetime_bound; /** * Cast this JSON value to a bool. * @@ -69793,11 +78344,27 @@ class document { * E.g., you must still call "is_null()" to check that a value is null even if * "type()" returns json_type::null. * + * The answer can be one of + * simdjson::ondemand::json_type::object, + * simdjson::ondemand::json_type::array, + * simdjson::ondemand::json_type::string, + * simdjson::ondemand::json_type::number, + * simdjson::ondemand::json_type::boolean, + * simdjson::ondemand::json_type::null. + * + * Starting with simdjson 4.0, this function will return simdjson::ondemand::json_type::unknown + * given a bad token. + * This allows you to identify a case such as {"key": NaN} and identify the NaN value. + * The simdjson::ondemand::json_type::unknown value should only happen with non-valid JSON. + * * NOTE: If you're only expecting a value to be one type (a typical case), it's generally * better to just call .get_double, .get_string, etc. and check for INCORRECT_TYPE (or just * let it throw an exception). * - * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". + * Prior to simdjson 4.0, this function would return an error given a bad token. + * Starting with simdjson 4.0, it will return simdjson::ondemand::json_type::unknown. + * This allows you to identify a case such as {"key": NaN} and identify the NaN value. + * The simdjson::ondemand::json_type::unknown value should only happen with non-valid JSON. */ simdjson_inline simdjson_result type() noexcept; @@ -70021,11 +78588,41 @@ class document { * the JSON document. */ simdjson_inline simdjson_result raw_json() noexcept; + +#if SIMDJSON_STATIC_REFLECTION + /** + * Extract only specific fields from the JSON object into a struct. + * + * This allows selective deserialization of only the fields you need, + * potentially improving performance by skipping unwanted fields. + * + * Example: + * ```c++ + * struct Car { + * std::string make; + * std::string model; + * int year; + * double price; + * }; + * + * Car car; + * doc.extract_into<"make", "model">(car); + * // Only 'make' and 'model' fields are extracted from JSON + * ``` + * + * @tparam FieldNames Compile-time string literals specifying which fields to extract + * @param out The output struct to populate with selected fields + * @returns SUCCESS on success, or an error code if a required field is missing or has wrong type + */ + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept; +#endif // SIMDJSON_STATIC_REFLECTION protected: /** * Consumes the document. */ - simdjson_inline error_code consume() noexcept; + simdjson_warn_unused simdjson_inline error_code consume() noexcept; simdjson_inline document(ondemand::json_iterator &&iter) noexcept; simdjson_inline const uint8_t *text(uint32_t idx) const noexcept; @@ -70078,7 +78675,7 @@ class document_reference { simdjson_inline simdjson_result get_double_in_string() noexcept; simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; simdjson_inline simdjson_result get_wobbly_string() noexcept; simdjson_inline simdjson_result get_raw_json_string() noexcept; simdjson_inline simdjson_result get_bool() noexcept; @@ -70087,7 +78684,7 @@ class document_reference { simdjson_inline simdjson_result is_null() noexcept; template simdjson_inline simdjson_result get() & -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept @@ -70100,7 +78697,7 @@ class document_reference { } template simdjson_inline simdjson_result get() && -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept @@ -70122,14 +78719,14 @@ class document_reference { * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. */ template - simdjson_inline error_code get(T &out) & -#if SIMDJSON_SUPPORTS_DESERIALIZATION + simdjson_warn_unused simdjson_inline error_code get(T &out) & +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept #endif { -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS if constexpr (custom_deserializable) { return deserialize(*this, out); } else { @@ -70141,7 +78738,7 @@ class document_reference { static_cast(out); // to get rid of unused errors return UNINITIALIZED; } -#else // SIMDJSON_SUPPORTS_DESERIALIZATION +#else // SIMDJSON_SUPPORTS_CONCEPTS // Unless the simdjson library or the user provides an inline implementation, calling this method should // immediately fail. static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " @@ -70151,12 +78748,17 @@ class document_reference { " You may also add support for custom types, see our documentation."); static_cast(out); // to get rid of unused errors return UNINITIALIZED; -#endif // SIMDJSON_SUPPORTS_DESERIALIZATION +#endif // SIMDJSON_SUPPORTS_CONCEPTS } /** @overload template error_code get(T &out) & noexcept */ template simdjson_inline error_code get(T &out) && noexcept; simdjson_inline simdjson_result raw_json() noexcept; +#if SIMDJSON_STATIC_REFLECTION + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept; +#endif // SIMDJSON_STATIC_REFLECTION simdjson_inline operator document&() const noexcept; #if SIMDJSON_EXCEPTIONS template @@ -70225,7 +78827,7 @@ struct simdjson_result : public icelake::implementa simdjson_inline simdjson_result get_double_in_string() noexcept; simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; simdjson_inline simdjson_result get_wobbly_string() noexcept; simdjson_inline simdjson_result get_raw_json_string() noexcept; simdjson_inline simdjson_result get_bool() noexcept; @@ -70238,6 +78840,9 @@ struct simdjson_result : public icelake::implementa template simdjson_inline error_code get(T &out) & noexcept; template simdjson_inline error_code get(T &out) && noexcept; #if SIMDJSON_EXCEPTIONS + + using icelake::implementation_simdjson_result_base::operator*; + using icelake::implementation_simdjson_result_base::operator->; template ::value == false>::type> explicit simdjson_inline operator T() noexcept(false); simdjson_inline operator icelake::ondemand::array() & noexcept(false); @@ -70277,6 +78882,11 @@ struct simdjson_result : public icelake::implementa simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; +#if SIMDJSON_STATIC_REFLECTION + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept; +#endif // SIMDJSON_STATIC_REFLECTION }; @@ -70303,7 +78913,7 @@ struct simdjson_result : public icelake:: simdjson_inline simdjson_result get_double_in_string() noexcept; simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; simdjson_inline simdjson_result get_wobbly_string() noexcept; simdjson_inline simdjson_result get_raw_json_string() noexcept; simdjson_inline simdjson_result get_bool() noexcept; @@ -70354,6 +78964,11 @@ struct simdjson_result : public icelake:: simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; +#if SIMDJSON_STATIC_REFLECTION + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept; +#endif // SIMDJSON_STATIC_REFLECTION }; @@ -70496,6 +79111,7 @@ class document_stream { * Default constructor. */ simdjson_inline iterator() noexcept; + simdjson_inline iterator(const iterator &other) noexcept = default; /** * Get the current document (or error). */ @@ -70509,6 +79125,7 @@ class document_stream { * @param other the end iterator to compare to. */ simdjson_inline bool operator!=(const iterator &other) const noexcept; + simdjson_inline bool operator==(const iterator &other) const noexcept; /** * @private * @@ -70552,6 +79169,11 @@ class document_stream { */ inline error_code error() const noexcept; + /** + * Returns whether the iterator is at the end. + */ + inline bool at_end() const noexcept; + private: simdjson_inline iterator(document_stream *s, bool finished) noexcept; /** The document_stream we're iterating through. */ @@ -70563,6 +79185,7 @@ class document_stream { friend class document_stream; friend class json_iterator; }; + using iterator = document_stream::iterator; /** * Start iterating the documents in the stream. @@ -70826,6 +79449,9 @@ struct simdjson_result : public icelake::implementatio /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */ +/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION && SIMDJSON_SUPPORTS_CONCEPTS */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_string_builder.h" // for constevalutil::fixed_string */ +/* amalgamation skipped (editor-only): #endif */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { @@ -71023,11 +79649,71 @@ class object { */ simdjson_inline simdjson_result raw_json() noexcept; +#if SIMDJSON_SUPPORTS_CONCEPTS + /** + * Get this object as the given type. + * + * @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized. + * @returns INCORRECT_TYPE If the JSON object is not of the given type. + * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. + */ + template + simdjson_warn_unused simdjson_inline error_code get(T &out) + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) { + static_assert(custom_deserializable); + return deserialize(*this, out); + } + /** + * Get this array as the given type. + * + * @returns A value of the given type, parsed from the JSON. + * @returns INCORRECT_TYPE If the JSON value is not the given type. + */ + template + simdjson_inline simdjson_result get() + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) + { + static_assert(std::is_default_constructible::value, "The specified type is not default constructible."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } + +#if SIMDJSON_STATIC_REFLECTION + /** + * Extract only specific fields from the JSON object into a struct. + * + * This allows selective deserialization of only the fields you need, + * potentially improving performance by skipping unwanted fields. + * + * Example: + * ```c++ + * struct Car { + * std::string make; + * std::string model; + * int year; + * double price; + * }; + * + * Car car; + * object.extract_into<"make", "model">(car); + * // Only 'make' and 'model' fields are extracted from JSON + * ``` + * + * @tparam FieldNames Compile-time string literals specifying which fields to extract + * @param out The output struct to populate with selected fields + * @returns SUCCESS on success, or an error code if a required field is missing or has wrong type + */ + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept; +#endif // SIMDJSON_STATIC_REFLECTION +#endif // SIMDJSON_SUPPORTS_CONCEPTS protected: /** * Go to the end of the object, no matter where you are right now. */ - simdjson_inline error_code consume() noexcept; + simdjson_warn_unused simdjson_inline error_code consume() noexcept; static simdjson_inline simdjson_result start(value_iterator &iter) noexcept; static simdjson_inline simdjson_result start_root(value_iterator &iter) noexcept; static simdjson_inline simdjson_result started(value_iterator &iter) noexcept; @@ -71066,12 +79752,42 @@ struct simdjson_result : public icelake::implementati simdjson_inline simdjson_result operator[](std::string_view key) && noexcept; simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; - inline simdjson_result reset() noexcept; inline simdjson_result is_empty() noexcept; inline simdjson_result count_fields() & noexcept; inline simdjson_result raw_json() noexcept; + #if SIMDJSON_SUPPORTS_CONCEPTS + // TODO: move this code into object-inl.h + template + simdjson_inline simdjson_result get() noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + return first; + } + return first.get(); + } + template + simdjson_warn_unused simdjson_inline error_code get(T& out) noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + out = first; + } else { + SIMDJSON_TRY( first.get(out) ); + } + return SUCCESS; + } + +#if SIMDJSON_STATIC_REFLECTION + // TODO: move this code into object-inl.h + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) noexcept { + if (error()) { return error(); } + return first.extract_into(out); + } +#endif // SIMDJSON_STATIC_REFLECTION +#endif // SIMDJSON_SUPPORTS_CONCEPTS }; } // namespace simdjson @@ -71200,6 +79916,20 @@ inline simdjson_result to_json_string(simdjson_result to_json_string(simdjson_result x); inline simdjson_result to_json_string(simdjson_result x); inline simdjson_result to_json_string(simdjson_result x); + +#if SIMDJSON_STATIC_REFLECTION +/** + * Create a JSON string from any user-defined type using static reflection. + * Only available when SIMDJSON_STATIC_REFLECTION is enabled. + */ +template + requires(!std::same_as && + !std::same_as && + !std::same_as && + !std::same_as) +inline std::string to_json_string(const T& obj); +#endif + } // namespace simdjson /** @@ -71271,28 +80001,30 @@ inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result #include +#if SIMDJSON_STATIC_REFLECTION +#include +// #include // for std::define_static_string - header not available yet +#endif namespace simdjson { -template -constexpr bool require_custom_serialization = false; ////////////////////////////// // Number deserialization ////////////////////////////// template - requires(!require_custom_serialization) error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { using limits = std::numeric_limits; @@ -71306,7 +80038,6 @@ error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { } template - requires(!require_custom_serialization) error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { double x; SIMDJSON_TRY(val.get_double().get(x)); @@ -71315,7 +80046,6 @@ error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { } template - requires(!require_custom_serialization) error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { using limits = std::numeric_limits; @@ -71328,8 +80058,23 @@ error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { return SUCCESS; } +////////////////////////////// +// String deserialization +////////////////////////////// + +// just a character! +error_code tag_invoke(deserialize_tag, auto &val, char &out) noexcept { + std::string_view x; + SIMDJSON_TRY(val.get_string().get(x)); + if(x.size() != 1) { + return INCORRECT_TYPE; + } + out = x[0]; + return SUCCESS; +} + +// any string-like type (can be constructed from std::string_view) template - requires(!require_custom_serialization) error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(std::is_nothrow_constructible_v) { std::string_view str; SIMDJSON_TRY(val.get_string().get(str)); @@ -71346,7 +80091,6 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(std::is_nothr * doc.get>(). */ template - requires(!require_custom_serialization) error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { using value_type = typename std::remove_cvref_t::value_type; static_assert( @@ -71355,9 +80099,13 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { static_assert( std::is_default_constructible_v, "The specified type inside the container must default constructible."); - icelake::ondemand::array arr; - SIMDJSON_TRY(val.get_array().get(arr)); + if constexpr (std::is_same_v, icelake::ondemand::array>) { + arr = val; + } else { + SIMDJSON_TRY(val.get_array().get(arr)); + } + for (auto v : arr) { if constexpr (concepts::returns_reference) { if (auto const err = v.get().get(concepts::emplace_one(out)); @@ -71388,7 +80136,6 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { * string-keyed types. */ template - requires(!require_custom_serialization) error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { using value_type = typename std::remove_cvref_t::mapped_type; static_assert( @@ -71414,7 +80161,45 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { return SUCCESS; } +template +error_code tag_invoke(deserialize_tag, icelake::ondemand::object &obj, T &out) noexcept { + using value_type = typename std::remove_cvref_t::mapped_type; + + out.clear(); + for (auto field : obj) { + std::string_view key; + SIMDJSON_TRY(field.unescaped_key().get(key)); + + icelake::ondemand::value value_obj; + SIMDJSON_TRY(field.value().get(value_obj)); + + value_type this_value; + SIMDJSON_TRY(value_obj.get(this_value)); + out.emplace(typename T::key_type(key), std::move(this_value)); + } + return SUCCESS; +} + +template +error_code tag_invoke(deserialize_tag, icelake::ondemand::value &val, T &out) noexcept { + icelake::ondemand::object obj; + SIMDJSON_TRY(val.get_object().get(obj)); + return simdjson::deserialize(obj, out); +} + +template +error_code tag_invoke(deserialize_tag, icelake::ondemand::document &doc, T &out) noexcept { + icelake::ondemand::object obj; + SIMDJSON_TRY(doc.get_object().get(obj)); + return simdjson::deserialize(obj, out); +} +template +error_code tag_invoke(deserialize_tag, icelake::ondemand::document_reference &doc, T &out) noexcept { + icelake::ondemand::object obj; + SIMDJSON_TRY(doc.get_object().get(obj)); + return simdjson::deserialize(obj, out); +} /** @@ -71432,7 +80217,6 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { * @return status of the conversion */ template - requires(!require_custom_serialization) error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deserializable::element_type, ValT>) { using element_type = typename std::remove_cvref_t::element_type; @@ -71457,17 +80241,17 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deser /** * This CPO (Customization Point Object) will help deserialize into optional types. */ -template - requires(!require_custom_serialization) -error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deserializable::value_type, ValT>) { +template +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept(nothrow_deserializable::value_type, decltype(val)>) { using value_type = typename std::remove_cvref_t::value_type; - static_assert( - deserializable, - "The specified type inside the unique_ptr must itself be deserializable"); - static_assert( - std::is_default_constructible_v, - "The specified type inside the unique_ptr must default constructible."); + // Check if the value is null + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); // Set to nullopt + return SUCCESS; + } if (!out) { out.emplace(); @@ -71476,10 +80260,329 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deser return SUCCESS; } + +#if SIMDJSON_STATIC_REFLECTION + + +template +constexpr bool user_defined_type = (std::is_class_v +&& !std::is_same_v && !std::is_same_v && !concepts::optional_type && +!concepts::appendable_containers); + + +template + requires(user_defined_type && std::is_class_v) +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept { + icelake::ondemand::object obj; + if constexpr (std::is_same_v, icelake::ondemand::object>) { + obj = val; + } else { + SIMDJSON_TRY(val.get_object().get(obj)); + } + template for (constexpr auto mem : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) { + if constexpr (!std::meta::is_const(mem) && std::meta::is_public(mem)) { + constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem)); + if constexpr (concepts::optional_type) { + // for optional members, it's ok if the key is missing + auto error = obj[key].get(out.[:mem:]); + if (error && error != NO_SUCH_FIELD) { + if(error == NO_SUCH_FIELD) { + out.[:mem:].reset(); + continue; + } + return error; + } + } else { + // for non-optional members, the key must be present + SIMDJSON_TRY(obj[key].get(out.[:mem:])); + } + } + }; + return simdjson::SUCCESS; +} + +// Support for enum deserialization - deserialize from string representation using expand approach from P2996R12 +template + requires(std::is_enum_v) +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept { +#if SIMDJSON_STATIC_REFLECTION + std::string_view str; + SIMDJSON_TRY(val.get_string().get(str)); + constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T)); + template for (constexpr auto enum_val : enumerators) { + if (str == std::meta::identifier_of(enum_val)) { + out = [:enum_val:]; + return SUCCESS; + } + }; + + return INCORRECT_TYPE; +#else + // Fallback: deserialize as integer if reflection not available + std::underlying_type_t int_val; + SIMDJSON_TRY(val.get(int_val)); + out = static_cast(int_val); + return SUCCESS; +#endif +} + +template + requires(user_defined_type>) +error_code tag_invoke(deserialize_tag, simdjson_value &val, std::unique_ptr &out) noexcept { + if (!out) { + out = std::make_unique(); + if (!out) { + return MEMALLOC; + } + } + if (auto err = val.get(*out)) { + out.reset(); + return err; + } + return SUCCESS; +} + +template + requires(user_defined_type>) +error_code tag_invoke(deserialize_tag, simdjson_value &val, std::shared_ptr &out) noexcept { + if (!out) { + out = std::make_shared(); + if (!out) { + return MEMALLOC; + } + } + if (auto err = val.get(*out)) { + out.reset(); + return err; + } + return SUCCESS; +} + +#endif // SIMDJSON_STATIC_REFLECTION + +//////////////////////////////////////// +// Unique pointers +//////////////////////////////////////// +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_unique(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_bool().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_unique(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_int64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_unique(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_uint64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_unique(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_double().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_unique(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_string().get(*out)); + return SUCCESS; +} + + +//////////////////////////////////////// +// Shared pointers +//////////////////////////////////////// +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_shared(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_bool().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_shared(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_int64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_shared(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_uint64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_shared(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_double().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_shared(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_string().get(*out)); + return SUCCESS; +} + + +//////////////////////////////////////// +// Explicit optional specializations +//////////////////////////////////////// + +//////////////////////////////////////// +// Explicit smart pointer specializations for string and int types +//////////////////////////////////////// +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + // Check if the value is null + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); // Set to nullptr + return SUCCESS; + } + + if (!out) { + out = std::make_unique(); + } + std::string_view str; + SIMDJSON_TRY(val.get_string().get(str)); + *out = std::string{str}; + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + // Check if the value is null + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); // Set to nullptr + return SUCCESS; + } + + if (!out) { + out = std::make_shared(); + } + std::string_view str; + SIMDJSON_TRY(val.get_string().get(str)); + *out = std::string{str}; + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + // Check if the value is null + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); // Set to nullptr + return SUCCESS; + } + + if (!out) { + out = std::make_unique(); + } + int64_t temp; + SIMDJSON_TRY(val.get_int64().get(temp)); + *out = static_cast(temp); + return SUCCESS; +} + } // namespace simdjson #endif // SIMDJSON_ONDEMAND_DESERIALIZE_H -#endif // SIMDJSON_SUPPORTS_DESERIALIZATION +#endif // SIMDJSON_SUPPORTS_CONCEPTS /* end file simdjson/generic/ondemand/std_deserialize.h for icelake */ // Inline definitions @@ -71572,7 +80675,7 @@ simdjson_inline simdjson_result array::begin() noexcept { simdjson_inline simdjson_result array::end() noexcept { return array_iterator(iter); } -simdjson_inline error_code array::consume() noexcept { +simdjson_warn_unused simdjson_warn_unused simdjson_inline error_code array::consume() noexcept { auto error = iter.json_iter().skip_child(iter.depth()-1); if(error) { iter.abandon(); } return error; @@ -71763,6 +80866,9 @@ simdjson_inline array_iterator &array_iterator::operator++() noexcept { return *this; } +simdjson_inline bool array_iterator::at_end() const noexcept { + return iter.at_end(); +} } // namespace ondemand } // namespace icelake } // namespace simdjson @@ -71799,7 +80905,9 @@ simdjson_inline simdjson_result &simdjson_res ++(first); return *this; } - +simdjson_inline bool simdjson_result::at_end() const noexcept { + return !first.iter.is_valid() || first.at_end(); +} } // namespace simdjson #endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_INL_H @@ -71856,7 +80964,7 @@ simdjson_inline simdjson_result value::get_string(bool allow_r return iter.get_string(allow_replacement); } template -simdjson_inline error_code value::get_string(string_type& receiver, bool allow_replacement) noexcept { +simdjson_warn_unused simdjson_inline error_code value::get_string(string_type& receiver, bool allow_replacement) noexcept { return iter.get_string(receiver, allow_replacement); } simdjson_inline simdjson_result value::get_wobbly_string() noexcept { @@ -71898,15 +81006,15 @@ template<> simdjson_inline simdjson_result value::get() noexcept { retu template<> simdjson_inline simdjson_result value::get() noexcept { return get_bool(); } -template<> simdjson_inline error_code value::get(array& out) noexcept { return get_array().get(out); } -template<> simdjson_inline error_code value::get(object& out) noexcept { return get_object().get(out); } -template<> simdjson_inline error_code value::get(raw_json_string& out) noexcept { return get_raw_json_string().get(out); } -template<> simdjson_inline error_code value::get(std::string_view& out) noexcept { return get_string(false).get(out); } -template<> simdjson_inline error_code value::get(number& out) noexcept { return get_number().get(out); } -template<> simdjson_inline error_code value::get(double& out) noexcept { return get_double().get(out); } -template<> simdjson_inline error_code value::get(uint64_t& out) noexcept { return get_uint64().get(out); } -template<> simdjson_inline error_code value::get(int64_t& out) noexcept { return get_int64().get(out); } -template<> simdjson_inline error_code value::get(bool& out) noexcept { return get_bool().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(array& out) noexcept { return get_array().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(object& out) noexcept { return get_object().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(raw_json_string& out) noexcept { return get_raw_json_string().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(std::string_view& out) noexcept { return get_string(false).get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(number& out) noexcept { return get_number().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(double& out) noexcept { return get_double().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(uint64_t& out) noexcept { return get_uint64().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(int64_t& out) noexcept { return get_int64().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(bool& out) noexcept { return get_bool().get(out); } #if SIMDJSON_EXCEPTIONS template @@ -72503,7 +81611,7 @@ simdjson_inline simdjson_result document::get_string(bool allo return get_root_value_iterator().get_root_string(true, allow_replacement); } template -simdjson_inline error_code document::get_string(string_type& receiver, bool allow_replacement) noexcept { +simdjson_warn_unused simdjson_inline error_code document::get_string(string_type& receiver, bool allow_replacement) noexcept { return get_root_value_iterator().get_root_string(receiver, true, allow_replacement); } simdjson_inline simdjson_result document::get_wobbly_string() noexcept { @@ -72529,15 +81637,15 @@ template<> simdjson_inline simdjson_result document::get() & noexcept { template<> simdjson_inline simdjson_result document::get() & noexcept { return get_bool(); } template<> simdjson_inline simdjson_result document::get() & noexcept { return get_value(); } -template<> simdjson_inline error_code document::get(array& out) & noexcept { return get_array().get(out); } -template<> simdjson_inline error_code document::get(object& out) & noexcept { return get_object().get(out); } -template<> simdjson_inline error_code document::get(raw_json_string& out) & noexcept { return get_raw_json_string().get(out); } -template<> simdjson_inline error_code document::get(std::string_view& out) & noexcept { return get_string(false).get(out); } -template<> simdjson_inline error_code document::get(double& out) & noexcept { return get_double().get(out); } -template<> simdjson_inline error_code document::get(uint64_t& out) & noexcept { return get_uint64().get(out); } -template<> simdjson_inline error_code document::get(int64_t& out) & noexcept { return get_int64().get(out); } -template<> simdjson_inline error_code document::get(bool& out) & noexcept { return get_bool().get(out); } -template<> simdjson_inline error_code document::get(value& out) & noexcept { return get_value().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(array& out) & noexcept { return get_array().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(object& out) & noexcept { return get_object().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(raw_json_string& out) & noexcept { return get_raw_json_string().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(std::string_view& out) & noexcept { return get_string(false).get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(double& out) & noexcept { return get_double().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(uint64_t& out) & noexcept { return get_uint64().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(int64_t& out) & noexcept { return get_int64().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(bool& out) & noexcept { return get_bool().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(value& out) & noexcept { return get_value().get(out); } template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_raw_json_string(); } template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_string(false); } @@ -72557,8 +81665,8 @@ simdjson_inline document::operator object() & noexcept(false) { return get_objec simdjson_inline document::operator uint64_t() noexcept(false) { return get_uint64(); } simdjson_inline document::operator int64_t() noexcept(false) { return get_int64(); } simdjson_inline document::operator double() noexcept(false) { return get_double(); } -simdjson_inline document::operator std::string_view() noexcept(false) { return get_string(false); } -simdjson_inline document::operator raw_json_string() noexcept(false) { return get_raw_json_string(); } +simdjson_inline document::operator std::string_view() noexcept(false) simdjson_lifetime_bound { return get_string(false); } +simdjson_inline document::operator raw_json_string() noexcept(false) simdjson_lifetime_bound { return get_raw_json_string(); } simdjson_inline document::operator bool() noexcept(false) { return get_bool(); } simdjson_inline document::operator value() noexcept(false) { return get_value(); } @@ -72607,7 +81715,7 @@ simdjson_inline simdjson_result document::operator[](const char *key) & n return start_or_resume_object()[key]; } -simdjson_inline error_code document::consume() noexcept { +simdjson_warn_unused simdjson_inline error_code document::consume() noexcept { bool scalar = false; auto error = is_scalar().get(scalar); if(error) { return error; } @@ -72709,6 +81817,54 @@ simdjson_inline simdjson_result document::at_path(std::string_view json_p } } + + +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION + +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code document::extract_into(T& out) & noexcept { + // Helper to check if a field name matches any of the requested fields + auto should_extract = [](std::string_view field_name) constexpr -> bool { + return ((FieldNames.view() == field_name) || ...); + }; + + // Iterate through all members of T using reflection + template for (constexpr auto mem : std::define_static_array( + std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) { + + if constexpr (!std::meta::is_const(mem) && std::meta::is_public(mem)) { + constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem)); + + // Only extract this field if it's in our list of requested fields + if constexpr (should_extract(key)) { + // Try to find and extract the field + if constexpr (concepts::optional_type) { + // For optional fields, it's ok if they're missing + auto field_result = find_field_unordered(key); + if (!field_result.error()) { + auto error = field_result.get(out.[:mem:]); + if (error && error != NO_SUCH_FIELD) { + return error; + } + } else if (field_result.error() != NO_SUCH_FIELD) { + return field_result.error(); + } else { + out.[:mem:].reset(); + } + } else { + // For required fields (in the requested list), fail if missing + SIMDJSON_TRY((*this)[key].get(out.[:mem:])); + } + } + } + }; + + return SUCCESS; +} + +#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION + } // namespace ondemand } // namespace icelake } // namespace simdjson @@ -72816,7 +81972,7 @@ simdjson_inline simdjson_result simdjson_result -simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { if (error()) { return error(); } return first.get_string(receiver, allow_replacement); } @@ -72852,12 +82008,12 @@ simdjson_deprecated simdjson_inline simdjson_result simdjson_result(first).get(); } template -simdjson_inline error_code simdjson_result::get(T &out) & noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) & noexcept { if (error()) { return error(); } return first.get(out); } template -simdjson_inline error_code simdjson_result::get(T &out) && noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) && noexcept { if (error()) { return error(); } return std::forward(first).get(out); } @@ -72867,8 +82023,8 @@ template<> simdjson_deprecated simdjson_inline simdjson_result(first); } -template<> simdjson_inline error_code simdjson_result::get(icelake::ondemand::document &out) & noexcept = delete; -template<> simdjson_inline error_code simdjson_result::get(icelake::ondemand::document &out) && noexcept { +template<> simdjson_warn_unused simdjson_inline error_code simdjson_result::get(icelake::ondemand::document &out) & noexcept = delete; +template<> simdjson_warn_unused simdjson_inline error_code simdjson_result::get(icelake::ondemand::document &out) && noexcept { if (error()) { return error(); } out = std::forward(first); return SUCCESS; @@ -72986,6 +82142,15 @@ simdjson_inline simdjson_result simdjson_result + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code simdjson_result::extract_into(T& out) & noexcept { + if (error()) { return error(); } + return first.extract_into(out); +} +#endif // SIMDJSON_STATIC_REFLECTION + } // namespace simdjson @@ -73020,7 +82185,7 @@ simdjson_inline simdjson_result document_reference::get_double() noexcep simdjson_inline simdjson_result document_reference::get_double_in_string() noexcept { return doc->get_root_value_iterator().get_root_double(false); } simdjson_inline simdjson_result document_reference::get_string(bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(false, allow_replacement); } template -simdjson_inline error_code document_reference::get_string(string_type& receiver, bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(receiver, false, allow_replacement); } +simdjson_warn_unused simdjson_inline error_code document_reference::get_string(string_type& receiver, bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(receiver, false, allow_replacement); } simdjson_inline simdjson_result document_reference::get_wobbly_string() noexcept { return doc->get_root_value_iterator().get_root_wobbly_string(false); } simdjson_inline simdjson_result document_reference::get_raw_json_string() noexcept { return doc->get_root_value_iterator().get_root_raw_json_string(false); } simdjson_inline simdjson_result document_reference::get_bool() noexcept { return doc->get_root_value_iterator().get_root_bool(false); } @@ -73073,7 +82238,13 @@ simdjson_inline simdjson_result document_reference::at_pointer(std::strin simdjson_inline simdjson_result document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); } simdjson_inline simdjson_result document_reference::raw_json() noexcept { return doc->raw_json();} simdjson_inline document_reference::operator document&() const noexcept { return *doc; } - +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code document_reference::extract_into(T& out) & noexcept { + return doc->extract_into(out); +} +#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION } // namespace ondemand } // namespace icelake } // namespace simdjson @@ -73170,7 +82341,7 @@ simdjson_inline simdjson_result simdjson_result -simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { if (error()) { return error(); } return first.get_string(receiver, allow_replacement); } @@ -73205,12 +82376,12 @@ simdjson_inline simdjson_result simdjson_result(first).get(); } template -simdjson_inline error_code simdjson_result::get(T &out) & noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) & noexcept { if (error()) { return error(); } return first.get(out); } template -simdjson_inline error_code simdjson_result::get(T &out) && noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) && noexcept { if (error()) { return error(); } return std::forward(first).get(out); } @@ -73227,13 +82398,13 @@ simdjson_inline simdjson_result simdjson_result -simdjson_inline error_code simdjson_result::get(icelake::ondemand::document_reference &out) & noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(icelake::ondemand::document_reference &out) & noexcept { if (error()) { return error(); } out = first; return SUCCESS; } template <> -simdjson_inline error_code simdjson_result::get(icelake::ondemand::document_reference &out) && noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(icelake::ondemand::document_reference &out) && noexcept { if (error()) { return error(); } out = first; return SUCCESS; @@ -73321,7 +82492,14 @@ simdjson_inline simdjson_result simdjson_result + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code simdjson_result::extract_into(T& out) & noexcept { + if (error()) { return error(); } + return first.extract_into(out); +} +#endif // SIMDJSON_STATIC_REFLECTION } // namespace simdjson #endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H @@ -73512,10 +82690,19 @@ simdjson_inline document_stream::iterator& document_stream::iterator::operator++ return *this; } +simdjson_inline bool document_stream::iterator::at_end() const noexcept { + return finished; +} + + simdjson_inline bool document_stream::iterator::operator!=(const document_stream::iterator &other) const noexcept { return finished != other.finished; } +simdjson_inline bool document_stream::iterator::operator==(const document_stream::iterator &other) const noexcept { + return finished == other.finished; +} + simdjson_inline document_stream::iterator document_stream::begin() noexcept { start(); // If there are no documents, we're finished. @@ -73633,7 +82820,10 @@ inline void document_stream::next_document() noexcept { // Always set depth=1 at the start of document doc.iter._depth = 1; // consume comma if comma separated is allowed - if (allow_comma_separated) { doc.iter.consume_character(','); } + if (allow_comma_separated) { + error_code ignored = doc.iter.consume_character(','); + static_cast(ignored); // ignored on purpose + } // Resets the string buffer at the beginning, thus invalidating the strings. doc.iter._string_buf_loc = parser->string_buf.get(); doc.iter._root = doc.iter.position(); @@ -73879,7 +83069,7 @@ simdjson_inline simdjson_result simdjson_result -simdjson_inline error_code simdjson_result::unescaped_key(string_type &receiver, bool allow_replacement) noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::unescaped_key(string_type &receiver, bool allow_replacement) noexcept { if (error()) { return error(); } return first.unescaped_key(receiver, allow_replacement); } @@ -74115,6 +83305,8 @@ simdjson_inline void json_iterator::assert_valid_position(token_position positio #ifndef SIMDJSON_CLANG_VISUAL_STUDIO SIMDJSON_ASSUME( position >= &parser->implementation->structural_indexes[0] ); SIMDJSON_ASSUME( position < &parser->implementation->structural_indexes[parser->implementation->n_structural_indexes] ); +#else + (void)position; // Suppress unused parameter warning #endif } @@ -74239,7 +83431,7 @@ simdjson_inline uint8_t *&json_iterator::string_buf_loc() noexcept { return _string_buf_loc; } -simdjson_inline error_code json_iterator::report_error(error_code _error, const char *message) noexcept { +simdjson_warn_unused simdjson_inline error_code json_iterator::report_error(error_code _error, const char *message) noexcept { SIMDJSON_ASSUME(_error != SUCCESS && _error != UNINITIALIZED && _error != INCORRECT_TYPE && _error != NO_SUCH_FIELD); logger::log_error(*this, message); error = _error; @@ -74283,7 +83475,7 @@ simdjson_inline void json_iterator::reenter_child(token_position position, depth _depth = child_depth; } -simdjson_inline error_code json_iterator::consume_character(char c) noexcept { +simdjson_warn_unused simdjson_inline error_code json_iterator::consume_character(char c) noexcept { if (*peek() == c) { return_current_and_advance(); return SUCCESS; @@ -74306,7 +83498,7 @@ simdjson_inline void json_iterator::set_start_position(depth_t depth, token_posi #endif -simdjson_inline error_code json_iterator::optional_error(error_code _error, const char *message) noexcept { +simdjson_warn_unused simdjson_inline error_code json_iterator::optional_error(error_code _error, const char *message) noexcept { SIMDJSON_ASSUME(_error == INCORRECT_TYPE || _error == NO_SUCH_FIELD); logger::log_error(*this, message); return _error; @@ -74701,6 +83893,10 @@ inline void log_line(const json_iterator &iter, token_position index, depth_t de /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value-inl.h" */ +/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_string_builder.h" // for constevalutil::fixed_string */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #endif */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { @@ -74758,7 +83954,7 @@ simdjson_inline simdjson_result object::start_root(value_iterator &iter) SIMDJSON_TRY( iter.start_root_object().error() ); return object(iter); } -simdjson_inline error_code object::consume() noexcept { +simdjson_warn_unused simdjson_inline error_code object::consume() noexcept { if(iter.is_at_key()) { /** * whenever you are pointing at a key, calling skip_child() is @@ -74887,6 +84083,52 @@ simdjson_inline simdjson_result object::reset() & noexcept { return iter.reset_object(); } +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION + +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code object::extract_into(T& out) & noexcept { + // Helper to check if a field name matches any of the requested fields + auto should_extract = [](std::string_view field_name) constexpr -> bool { + return ((FieldNames.view() == field_name) || ...); + }; + + // Iterate through all members of T using reflection + template for (constexpr auto mem : std::define_static_array( + std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) { + + if constexpr (!std::meta::is_const(mem) && std::meta::is_public(mem)) { + constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem)); + + // Only extract this field if it's in our list of requested fields + if constexpr (should_extract(key)) { + // Try to find and extract the field + if constexpr (concepts::optional_type) { + // For optional fields, it's ok if they're missing + auto field_result = find_field_unordered(key); + if (!field_result.error()) { + auto error = field_result.get(out.[:mem:]); + if (error && error != NO_SUCH_FIELD) { + return error; + } + } else if (field_result.error() != NO_SUCH_FIELD) { + return field_result.error(); + } else { + out.[:mem:].reset(); + } + } else { + // For required fields (in the requested list), fail if missing + SIMDJSON_TRY((*this)[key].get(out.[:mem:])); + } + } + } + }; + + return SUCCESS; +} + +#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION + } // namespace ondemand } // namespace icelake } // namespace simdjson @@ -74963,6 +84205,7 @@ simdjson_inline simdjson_result simdjson_result parser::iterate(padded_string_view json) & noexcept { - if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; } + if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; } json.remove_utf8_bom(); @@ -75177,7 +84420,7 @@ simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(p #ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON simdjson_warn_unused simdjson_inline simdjson_result parser::iterate_allow_incomplete_json(padded_string_view json) & noexcept { - if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; } + if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; } json.remove_utf8_bom(); @@ -75209,10 +84452,7 @@ simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(s } simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(std::string &json) & noexcept { - if(json.capacity() - json.size() < SIMDJSON_PADDING) { - json.reserve(json.size() + SIMDJSON_PADDING); - } - return iterate(padded_string_view(json)); + return iterate(pad_with_reserve(json)); } simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(const std::string &json) & noexcept { @@ -75234,7 +84474,7 @@ simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(c } simdjson_warn_unused simdjson_inline simdjson_result parser::iterate_raw(padded_string_view json) & noexcept { - if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; } + if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; } json.remove_utf8_bom(); @@ -75249,6 +84489,7 @@ simdjson_warn_unused simdjson_inline simdjson_result parser::iter } inline simdjson_result parser::iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept { + // Warning: no check is done on the buffer padding. We trust the user. if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; } if((len >= 3) && (std::memcmp(buf, "\xEF\xBB\xBF", 3) == 0)) { buf += 3; @@ -75257,16 +84498,24 @@ inline simdjson_result parser::iterate_many(const uint8_t *buf, if(allow_comma_separated && batch_size < len) { batch_size = len; } return document_stream(*this, buf, len, batch_size, allow_comma_separated); } + inline simdjson_result parser::iterate_many(const char *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept { + // Warning: no check is done on the buffer padding. We trust the user. return iterate_many(reinterpret_cast(buf), len, batch_size, allow_comma_separated); } -inline simdjson_result parser::iterate_many(const std::string &s, size_t batch_size, bool allow_comma_separated) noexcept { +inline simdjson_result parser::iterate_many(padded_string_view s, size_t batch_size, bool allow_comma_separated) noexcept { + if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; } return iterate_many(s.data(), s.length(), batch_size, allow_comma_separated); } inline simdjson_result parser::iterate_many(const padded_string &s, size_t batch_size, bool allow_comma_separated) noexcept { - return iterate_many(s.data(), s.length(), batch_size, allow_comma_separated); + return iterate_many(padded_string_view(s), batch_size, allow_comma_separated); +} +inline simdjson_result parser::iterate_many(const std::string &s, size_t batch_size, bool allow_comma_separated) noexcept { + return iterate_many(padded_string_view(s), batch_size, allow_comma_separated); +} +inline simdjson_result parser::iterate_many(std::string &s, size_t batch_size, bool allow_comma_separated) noexcept { + return iterate_many(pad(s), batch_size, allow_comma_separated); } - simdjson_pure simdjson_inline size_t parser::capacity() const noexcept { return _capacity; } @@ -75301,6 +84550,34 @@ simdjson_inline simdjson_warn_unused simdjson_result parser::u return result; } +simdjson_inline simdjson_warn_unused ondemand::parser& parser::get_parser() { + return *parser::get_parser_instance(); +} + +simdjson_inline bool release_parser() { + auto &parser_instance = parser::get_threadlocal_parser_if_exists(); + if (parser_instance) { + parser_instance.reset(); + return true; + } + return false; +} + +simdjson_inline simdjson_warn_unused std::unique_ptr& parser::get_parser_instance() { + std::unique_ptr& parser_instance = get_threadlocal_parser_if_exists(); + if (!parser_instance) { + parser_instance.reset(new ondemand::parser()); + } + return parser_instance; +} + +simdjson_inline simdjson_warn_unused std::unique_ptr& parser::get_threadlocal_parser_if_exists() { + // @the-moisrex points out that this could be implemented with std::optional (C++17). + thread_local std::unique_ptr parser_instance = nullptr; + return parser_instance; +} + + } // namespace ondemand } // namespace icelake } // namespace simdjson @@ -75335,8 +84612,13 @@ namespace ondemand { simdjson_inline raw_json_string::raw_json_string(const uint8_t * _buf) noexcept : buf{_buf} {} -simdjson_inline const char * raw_json_string::raw() const noexcept { return reinterpret_cast(buf); } +simdjson_inline const char * raw_json_string::raw() const noexcept { + return reinterpret_cast(buf); +} +simdjson_inline char raw_json_string::operator[](size_t i) const noexcept { + return reinterpret_cast(buf)[i]; +} simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(std::string_view target) noexcept { size_t pos{0}; @@ -75513,6 +84795,10 @@ simdjson_inline simdjson_result simdjson_result::operator[](size_t i) const noexcept { + if (error()) { return error(); } + return first[i]; +} simdjson_inline simdjson_warn_unused simdjson_result simdjson_result::unescape(icelake::ondemand::json_iterator &iter, bool allow_replacement) const noexcept { if (error()) { return error(); } return first.unescape(iter, allow_replacement); @@ -75538,6 +84824,9 @@ simdjson_inline simdjson_warn_unused simdjson_result simdjson_ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/serialization.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */ +/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_builder.h" */ +/* amalgamation skipped (editor-only): #endif */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { @@ -75903,7 +85192,7 @@ simdjson_warn_unused simdjson_inline simdjson_result value_iterator::start if (*_json_iter->peek() == '}') { logger::log_value(*_json_iter, "empty object"); _json_iter->return_current_and_advance(); - end_container(); + SIMDJSON_TRY(end_container()); return false; } return true; @@ -76757,7 +86046,7 @@ simdjson_inline void value_iterator::advance_scalar(const char *type) noexcept { _json_iter->ascend_to(depth()-1); } -simdjson_inline error_code value_iterator::start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept { +simdjson_warn_unused simdjson_inline error_code value_iterator::start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept { logger::log_start_value(*_json_iter, start_position(), depth(), type); // If we're not at the position anymore, we don't want to advance the cursor. const uint8_t *json; @@ -76919,7 +86208,7 @@ simdjson_inline simdjson_result value_iterator::type() const noexcept case '5': case '6': case '7': case '8': case '9': return json_type::number; default: - return TAPE_ERROR; + return json_type::unknown; } } @@ -76959,6 +86248,1097 @@ simdjson_inline simdjson_result::simdjson_res #endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H /* end file simdjson/generic/ondemand/value_iterator-inl.h for icelake */ +// JSON builder inline definitions +/* including simdjson/generic/ondemand/json_string_builder-inl.h for icelake: #include "simdjson/generic/ondemand/json_string_builder-inl.h" */ +/* begin file simdjson/generic/ondemand/json_string_builder-inl.h for icelake */ +/** + * This file is part of the builder API. It is temporarily in the ondemand + * directory but we will move it to a builder directory later. + */ +#include +#include +#include +#ifndef SIMDJSON_GENERIC_STRING_BUILDER_INL_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_INL_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_string_builder.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +/* + * Empirically, we have found that an inlined optimization is important for + * performance. The following macros are not ideal. We should find a better + * way to inline the code. + */ + +#if defined(__SSE2__) || defined(__x86_64__) || defined(__x86_64) || \ + (defined(_M_AMD64) || defined(_M_X64) || \ + (defined(_M_IX86_FP) && _M_IX86_FP == 2)) +#ifndef SIMDJSON_EXPERIMENTAL_HAS_SSE2 +#define SIMDJSON_EXPERIMENTAL_HAS_SSE2 1 +#endif +#endif + +#if defined(__aarch64__) || defined(_M_ARM64) +#ifndef SIMDJSON_EXPERIMENTAL_HAS_NEON +#define SIMDJSON_EXPERIMENTAL_HAS_NEON 1 +#endif +#endif +#if SIMDJSON_EXPERIMENTAL_HAS_NEON +#include +#endif +#if SIMDJSON_EXPERIMENTAL_HAS_SSE2 +#include +#endif + +namespace simdjson { +namespace icelake { +namespace builder { + +static SIMDJSON_CONSTEXPR_LAMBDA std::array + json_quotable_character = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 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, 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, 1, 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, 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, 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, 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, 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, 0, 0, 0}; + +/** + +A possible SWAR implementation of has_json_escapable_byte. It is not used +because it is slower than the current implementation. It is kept here for +reference (to show that we tried it). + +inline bool has_json_escapable_byte(uint64_t x) { + uint64_t is_ascii = 0x8080808080808080ULL & ~x; + uint64_t xor2 = x ^ 0x0202020202020202ULL; + uint64_t lt32_or_eq34 = xor2 - 0x2121212121212121ULL; + uint64_t sub92 = x ^ 0x5C5C5C5C5C5C5C5CULL; + uint64_t eq92 = (sub92 - 0x0101010101010101ULL); + return ((lt32_or_eq34 | eq92) & is_ascii) != 0; +} + +**/ + +SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline bool +simple_needs_escaping(std::string_view v) { + for (char c : v) { + // a table lookup is faster than a series of comparisons + if (json_quotable_character[static_cast(c)]) { + return true; + } + } + return false; +} + +#if SIMDJSON_EXPERIMENTAL_HAS_NEON +simdjson_inline bool fast_needs_escaping(std::string_view view) { + if (view.size() < 16) { + return simple_needs_escaping(view); + } + size_t i = 0; + uint8x16_t running = vdupq_n_u8(0); + uint8x16_t v34 = vdupq_n_u8(34); + uint8x16_t v92 = vdupq_n_u8(92); + + for (; i + 15 < view.size(); i += 16) { + uint8x16_t word = vld1q_u8((const uint8_t *)view.data() + i); + running = vorrq_u8(running, vceqq_u8(word, v34)); + running = vorrq_u8(running, vceqq_u8(word, v92)); + running = vorrq_u8(running, vcltq_u8(word, vdupq_n_u8(32))); + } + if (i < view.size()) { + uint8x16_t word = + vld1q_u8((const uint8_t *)view.data() + view.length() - 16); + running = vorrq_u8(running, vceqq_u8(word, v34)); + running = vorrq_u8(running, vceqq_u8(word, v92)); + running = vorrq_u8(running, vcltq_u8(word, vdupq_n_u8(32))); + } + return vmaxvq_u32(vreinterpretq_u32_u8(running)) != 0; +} +#elif SIMDJSON_EXPERIMENTAL_HAS_SSE2 +simdjson_inline bool fast_needs_escaping(std::string_view view) { + if (view.size() < 16) { + return simple_needs_escaping(view); + } + size_t i = 0; + __m128i running = _mm_setzero_si128(); + for (; i + 15 < view.size(); i += 16) { + + __m128i word = + _mm_loadu_si128(reinterpret_cast(view.data() + i)); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(34))); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(92))); + running = _mm_or_si128( + running, _mm_cmpeq_epi8(_mm_subs_epu8(word, _mm_set1_epi8(31)), + _mm_setzero_si128())); + } + if (i < view.size()) { + __m128i word = _mm_loadu_si128( + reinterpret_cast(view.data() + view.length() - 16)); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(34))); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(92))); + running = _mm_or_si128( + running, _mm_cmpeq_epi8(_mm_subs_epu8(word, _mm_set1_epi8(31)), + _mm_setzero_si128())); + } + return _mm_movemask_epi8(running) != 0; +} +#else +simdjson_inline bool fast_needs_escaping(std::string_view view) { + return simple_needs_escaping(view); +} +#endif + +SIMDJSON_CONSTEXPR_LAMBDA inline size_t +find_next_json_quotable_character(const std::string_view view, + size_t location) noexcept { + + for (auto pos = view.begin() + location; pos != view.end(); ++pos) { + if (json_quotable_character[static_cast(*pos)]) { + return pos - view.begin(); + } + } + return size_t(view.size()); +} + +SIMDJSON_CONSTEXPR_LAMBDA static std::string_view control_chars[] = { + "\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", "\\u0006", + "\\u0007", "\\b", "\\t", "\\n", "\\u000b", "\\f", "\\r", + "\\u000e", "\\u000f", "\\u0010", "\\u0011", "\\u0012", "\\u0013", "\\u0014", + "\\u0015", "\\u0016", "\\u0017", "\\u0018", "\\u0019", "\\u001a", "\\u001b", + "\\u001c", "\\u001d", "\\u001e", "\\u001f"}; + +// All Unicode characters may be placed within the quotation marks, except for +// the characters that MUST be escaped: quotation mark, reverse solidus, and the +// control characters (U+0000 through U+001F). There are two-character sequence +// escape representations of some popular characters: +// \", \\, \b, \f, \n, \r, \t. +SIMDJSON_CONSTEXPR_LAMBDA void escape_json_char(char c, char *&out) { + if (c == '"') { + memcpy(out, "\\\"", 2); + out += 2; + } else if (c == '\\') { + memcpy(out, "\\\\", 2); + out += 2; + } else { + std::string_view v = control_chars[uint8_t(c)]; + memcpy(out, v.data(), v.size()); + out += v.size(); + } +} + +inline size_t write_string_escaped(const std::string_view input, char *out) { + size_t mysize = input.size(); + if (!fast_needs_escaping(input)) { // fast path! + memcpy(out, input.data(), input.size()); + return input.size(); + } + const char *const initout = out; + size_t location = find_next_json_quotable_character(input, 0); + memcpy(out, input.data(), location); + out += location; + escape_json_char(input[location], out); + location += 1; + while (location < mysize) { + size_t newlocation = find_next_json_quotable_character(input, location); + memcpy(out, input.data() + location, newlocation - location); + out += newlocation - location; + location = newlocation; + if (location == mysize) { + break; + } + escape_json_char(input[location], out); + location += 1; + } + return out - initout; +} + +simdjson_inline string_builder::string_builder(size_t initial_capacity) + : buffer(new(std::nothrow) char[initial_capacity]), position(0), + capacity(buffer.get() != nullptr ? initial_capacity : 0), + is_valid(buffer.get() != nullptr) {} + +simdjson_inline bool string_builder::capacity_check(size_t upcoming_bytes) { + // We use the convention that when is_valid is false, then the capacity and + // the position are 0. + // Most of the time, this function will return true. + if (simdjson_likely(upcoming_bytes <= capacity - position)) { + return true; + } + // check for overflow, most of the time there is no overflow + if (simdjson_likely(position + upcoming_bytes < position)) { + return false; + } + // We will rarely get here. + grow_buffer((std::max)(capacity * 2, position + upcoming_bytes)); + // If the buffer allocation failed, we set is_valid to false. + return is_valid; +} + +simdjson_inline void string_builder::grow_buffer(size_t desired_capacity) { + if (!is_valid) { + return; + } + std::unique_ptr new_buffer(new (std::nothrow) char[desired_capacity]); + if (new_buffer.get() == nullptr) { + set_valid(false); + return; + } + std::memcpy(new_buffer.get(), buffer.get(), position); + buffer.swap(new_buffer); + capacity = desired_capacity; +} + +simdjson_inline void string_builder::set_valid(bool valid) noexcept { + if (!valid) { + is_valid = false; + capacity = 0; + position = 0; + buffer.reset(); + } else { + is_valid = true; + } +} + +simdjson_inline size_t string_builder::size() const noexcept { + return position; +} + +simdjson_inline void string_builder::append(char c) noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = c; + } +} + +simdjson_inline void string_builder::append_null() noexcept { + constexpr char null_literal[] = "null"; + constexpr size_t null_len = sizeof(null_literal) - 1; + if (capacity_check(null_len)) { + std::memcpy(buffer.get() + position, null_literal, null_len); + position += null_len; + } +} + +simdjson_inline void string_builder::clear() noexcept { + position = 0; + // if it was invalid, we should try to repair it + if (!is_valid) { + capacity = 0; + buffer.reset(); + is_valid = true; + } +} + +namespace internal { + +template ::value>::type> +simdjson_really_inline int int_log2(number_type x) { + return 63 - leading_zeroes(uint64_t(x) | 1); +} + +simdjson_really_inline int fast_digit_count_32(uint32_t x) { + static uint64_t table[] = { + 4294967296, 8589934582, 8589934582, 8589934582, 12884901788, + 12884901788, 12884901788, 17179868184, 17179868184, 17179868184, + 21474826480, 21474826480, 21474826480, 21474826480, 25769703776, + 25769703776, 25769703776, 30063771072, 30063771072, 30063771072, + 34349738368, 34349738368, 34349738368, 34349738368, 38554705664, + 38554705664, 38554705664, 41949672960, 41949672960, 41949672960, + 42949672960, 42949672960}; + return uint32_t((x + table[int_log2(x)]) >> 32); +} + +simdjson_really_inline int fast_digit_count_64(uint64_t x) { + static uint64_t table[] = {9, + 99, + 999, + 9999, + 99999, + 999999, + 9999999, + 99999999, + 999999999, + 9999999999, + 99999999999, + 999999999999, + 9999999999999, + 99999999999999, + 999999999999999ULL, + 9999999999999999ULL, + 99999999999999999ULL, + 999999999999999999ULL, + 9999999999999999999ULL}; + int y = (19 * int_log2(x) >> 6); + y += x > table[y]; + return y + 1; +} + +template ::value>::type> +simdjson_really_inline size_t digit_count(number_type v) noexcept { + static_assert(sizeof(number_type) == 8 || sizeof(number_type) == 4 || + sizeof(number_type) == 2 || sizeof(number_type) == 1, + "We only support 8-bit, 16-bit, 32-bit and 64-bit numbers"); + SIMDJSON_IF_CONSTEXPR(sizeof(number_type) <= 4) { + return fast_digit_count_32(static_cast(v)); + } + else { + return fast_digit_count_64(static_cast(v)); + } +} +static const char decimal_table[200] = { + 0x30, 0x30, 0x30, 0x31, 0x30, 0x32, 0x30, 0x33, 0x30, 0x34, 0x30, 0x35, + 0x30, 0x36, 0x30, 0x37, 0x30, 0x38, 0x30, 0x39, 0x31, 0x30, 0x31, 0x31, + 0x31, 0x32, 0x31, 0x33, 0x31, 0x34, 0x31, 0x35, 0x31, 0x36, 0x31, 0x37, + 0x31, 0x38, 0x31, 0x39, 0x32, 0x30, 0x32, 0x31, 0x32, 0x32, 0x32, 0x33, + 0x32, 0x34, 0x32, 0x35, 0x32, 0x36, 0x32, 0x37, 0x32, 0x38, 0x32, 0x39, + 0x33, 0x30, 0x33, 0x31, 0x33, 0x32, 0x33, 0x33, 0x33, 0x34, 0x33, 0x35, + 0x33, 0x36, 0x33, 0x37, 0x33, 0x38, 0x33, 0x39, 0x34, 0x30, 0x34, 0x31, + 0x34, 0x32, 0x34, 0x33, 0x34, 0x34, 0x34, 0x35, 0x34, 0x36, 0x34, 0x37, + 0x34, 0x38, 0x34, 0x39, 0x35, 0x30, 0x35, 0x31, 0x35, 0x32, 0x35, 0x33, + 0x35, 0x34, 0x35, 0x35, 0x35, 0x36, 0x35, 0x37, 0x35, 0x38, 0x35, 0x39, + 0x36, 0x30, 0x36, 0x31, 0x36, 0x32, 0x36, 0x33, 0x36, 0x34, 0x36, 0x35, + 0x36, 0x36, 0x36, 0x37, 0x36, 0x38, 0x36, 0x39, 0x37, 0x30, 0x37, 0x31, + 0x37, 0x32, 0x37, 0x33, 0x37, 0x34, 0x37, 0x35, 0x37, 0x36, 0x37, 0x37, + 0x37, 0x38, 0x37, 0x39, 0x38, 0x30, 0x38, 0x31, 0x38, 0x32, 0x38, 0x33, + 0x38, 0x34, 0x38, 0x35, 0x38, 0x36, 0x38, 0x37, 0x38, 0x38, 0x38, 0x39, + 0x39, 0x30, 0x39, 0x31, 0x39, 0x32, 0x39, 0x33, 0x39, 0x34, 0x39, 0x35, + 0x39, 0x36, 0x39, 0x37, 0x39, 0x38, 0x39, 0x39, +}; +} // namespace internal + +template +simdjson_inline void string_builder::append(number_type v) noexcept { + static_assert(std::is_same::value || + std::is_integral::value || + std::is_floating_point::value, + "Unsupported number type"); + // If C++17 is available, we can 'if constexpr' here. + SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + if (v) { + constexpr char true_literal[] = "true"; + constexpr size_t true_len = sizeof(true_literal) - 1; + if (capacity_check(true_len)) { + std::memcpy(buffer.get() + position, true_literal, true_len); + position += true_len; + } + } else { + constexpr char false_literal[] = "false"; + constexpr size_t false_len = sizeof(false_literal) - 1; + if (capacity_check(false_len)) { + std::memcpy(buffer.get() + position, false_literal, false_len); + position += false_len; + } + } + } + else SIMDJSON_IF_CONSTEXPR(std::is_unsigned::value) { + constexpr size_t max_number_size = 20; + if (capacity_check(max_number_size)) { + using unsigned_type = typename std::make_unsigned::type; + unsigned_type pv = static_cast(v); + size_t dc = internal::digit_count(pv); + char *write_pointer = buffer.get() + position + dc - 1; + while (pv >= 100) { + memcpy(write_pointer - 1, &internal::decimal_table[(pv % 100) * 2], 2); + write_pointer -= 2; + pv /= 100; + } + if (pv >= 10) { + *write_pointer-- = char('0' + (pv % 10)); + pv /= 10; + } + *write_pointer = char('0' + pv); + position += dc; + } + } + else SIMDJSON_IF_CONSTEXPR(std::is_integral::value) { + constexpr size_t max_number_size = 20; + if (capacity_check(max_number_size)) { + using unsigned_type = typename std::make_unsigned::type; + bool negative = v < 0; + unsigned_type pv = static_cast(v); + if (negative) { + pv = 0 - pv; // the 0 is for Microsoft + } + size_t dc = internal::digit_count(pv); + if (negative) { + buffer.get()[position++] = '-'; + } + char *write_pointer = buffer.get() + position + dc - 1; + while (pv >= 100) { + memcpy(write_pointer - 1, &internal::decimal_table[(pv % 100) * 2], 2); + write_pointer -= 2; + pv /= 100; + } + if (pv >= 10) { + *write_pointer-- = char('0' + (pv % 10)); + pv /= 10; + } + *write_pointer = char('0' + pv); + position += dc; + } + } + else SIMDJSON_IF_CONSTEXPR(std::is_floating_point::value) { + constexpr size_t max_number_size = 24; + if (capacity_check(max_number_size)) { + // We could specialize for float. + char *end = simdjson::internal::to_chars(buffer.get() + position, nullptr, + double(v)); + position = end - buffer.get(); + } + } +} + +simdjson_inline void +string_builder::escape_and_append(std::string_view input) noexcept { + // escaping might turn a control character into \x00xx so 6 characters. + if (capacity_check(6 * input.size())) { + position += write_string_escaped(input, buffer.get() + position); + } +} + +simdjson_inline void +string_builder::escape_and_append_with_quotes(std::string_view input) noexcept { + // escaping might turn a control character into \x00xx so 6 characters. + if (capacity_check(2 + 6 * input.size())) { + buffer.get()[position++] = '"'; + position += write_string_escaped(input, buffer.get() + position); + buffer.get()[position++] = '"'; + } +} + +simdjson_inline void +string_builder::escape_and_append_with_quotes(char input) noexcept { + // escaping might turn a control character into \x00xx so 6 characters. + if (capacity_check(2 + 6 * 1)) { + buffer.get()[position++] = '"'; + std::string_view cinput(&input, 1); + position += write_string_escaped(cinput, buffer.get() + position); + buffer.get()[position++] = '"'; + } +} + +simdjson_inline void +string_builder::escape_and_append_with_quotes(const char *input) noexcept { + std::string_view cinput(input); + escape_and_append_with_quotes(cinput); +} +#if SIMDJSON_SUPPORTS_CONCEPTS +template +simdjson_inline void string_builder::escape_and_append_with_quotes() noexcept { + escape_and_append_with_quotes(constevalutil::string_constant::value); +} +#endif + +simdjson_inline void string_builder::append_raw(const char *c) noexcept { + size_t len = std::strlen(c); + append_raw(c, len); +} + +simdjson_inline void +string_builder::append_raw(std::string_view input) noexcept { + if (capacity_check(input.size())) { + std::memcpy(buffer.get() + position, input.data(), input.size()); + position += input.size(); + } +} + +simdjson_inline void string_builder::append_raw(const char *str, + size_t len) noexcept { + if (capacity_check(len)) { + std::memcpy(buffer.get() + position, str, len); + position += len; + } +} +#if SIMDJSON_SUPPORTS_CONCEPTS +// Support for optional types (std::optional, etc.) +template + requires(!require_custom_serialization) +simdjson_inline void string_builder::append(const T &opt) { + if (opt) { + append(*opt); + } else { + append_null(); + } +} + +template + requires(require_custom_serialization) +simdjson_inline void string_builder::append(const T &val) { + serialize(*this, val); +} + +template + requires(std::is_convertible::value || + std::is_same::value) +simdjson_inline void string_builder::append(const T &value) { + escape_and_append_with_quotes(value); +} +#endif + +#if SIMDJSON_SUPPORTS_RANGES && SIMDJSON_SUPPORTS_CONCEPTS +// Support for range-based appending (std::ranges::view, etc.) +template + requires(!std::is_convertible::value) +simdjson_inline void string_builder::append(const R &range) noexcept { + auto it = std::ranges::begin(range); + auto end = std::ranges::end(range); + if constexpr (concepts::is_pair) { + start_object(); + + if (it == end) { + end_object(); + return; // Handle empty range + } + // Append first item without leading comma + append_key_value(it->first, it->second); + ++it; + + // Append remaining items with preceding commas + for (; it != end; ++it) { + append_comma(); + append_key_value(it->first, it->second); + } + end_object(); + } else { + start_array(); + if (it == end) { + end_array(); + return; // Handle empty range + } + + // Append first item without leading comma + append(*it); + ++it; + + // Append remaining items with preceding commas + for (; it != end; ++it) { + append_comma(); + append(*it); + } + end_array(); + } +} + +#endif + +#if SIMDJSON_EXCEPTIONS +simdjson_inline string_builder::operator std::string() const noexcept(false) { + return std::string(operator std::string_view()); +} + +simdjson_inline string_builder::operator std::string_view() const + noexcept(false) simdjson_lifetime_bound { + return view(); +} +#endif + +simdjson_inline simdjson_result +string_builder::view() const noexcept { + if (!is_valid) { + return simdjson::OUT_OF_CAPACITY; + } + return std::string_view(buffer.get(), position); +} + +simdjson_inline simdjson_result string_builder::c_str() noexcept { + if (capacity_check(1)) { + buffer.get()[position] = '\0'; + return buffer.get(); + } + return simdjson::OUT_OF_CAPACITY; +} + +simdjson_inline bool string_builder::validate_unicode() const noexcept { + return simdjson::validate_utf8(buffer.get(), position); +} + +simdjson_inline void string_builder::start_object() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = '{'; + } +} + +simdjson_inline void string_builder::end_object() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = '}'; + } +} + +simdjson_inline void string_builder::start_array() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = '['; + } +} + +simdjson_inline void string_builder::end_array() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = ']'; + } +} + +simdjson_inline void string_builder::append_comma() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = ','; + } +} + +simdjson_inline void string_builder::append_colon() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = ':'; + } +} + +template +simdjson_inline void +string_builder::append_key_value(key_type key, value_type value) noexcept { + static_assert(std::is_same::value || + std::is_convertible::value, + "Unsupported key type"); + escape_and_append_with_quotes(key); + append_colon(); + SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + append_null(); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR( + std::is_convertible::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else { + append(value); + } +} + +#if SIMDJSON_SUPPORTS_CONCEPTS +template +simdjson_inline void +string_builder::append_key_value(value_type value) noexcept { + escape_and_append_with_quotes(); + append_colon(); + SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + append_null(); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR( + std::is_convertible::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else { + append(value); + } +} +#endif + +} // namespace builder +} // namespace icelake +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_STRING_BUILDER_INL_H +/* end file simdjson/generic/ondemand/json_string_builder-inl.h for icelake */ +/* including simdjson/generic/ondemand/json_builder.h for icelake: #include "simdjson/generic/ondemand/json_builder.h" */ +/* begin file simdjson/generic/ondemand/json_builder.h for icelake */ +/** + * This file is part of the builder API. It is temporarily in the ondemand directory + * but we will move it to a builder directory later. + */ +#ifndef SIMDJSON_GENERIC_BUILDER_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_string_builder.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/concepts.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ +#if SIMDJSON_STATIC_REFLECTION + +#include +#include +#include +#include +#include +#include +#include +#include +// #include // for std::define_static_string - header not available yet + +namespace simdjson { +namespace icelake { +namespace builder { + +template + requires(concepts::container_but_not_string && !require_custom_serialization) +constexpr void atom(string_builder &b, const T &t) { + auto it = t.begin(); + auto end = t.end(); + if (it == end) { + b.append_raw("[]"); + return; + } + b.append('['); + atom(b, *it); + ++it; + for (; it != end; ++it) { + b.append(','); + atom(b, *it); + } + b.append(']'); +} + +template + requires(std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v) +constexpr void atom(string_builder &b, const T &t) { + b.escape_and_append_with_quotes(t); +} + +template + requires(!require_custom_serialization) +constexpr void atom(string_builder &b, const T &m) { + if (m.empty()) { + b.append_raw("{}"); + return; + } + b.append('{'); + bool first = true; + for (const auto& [key, value] : m) { + if (!first) { + b.append(','); + } + first = false; + // Keys must be convertible to string_view per the concept + b.escape_and_append_with_quotes(key); + b.append(':'); + atom(b, value); + } + b.append('}'); +} + + +template::value && !std::is_same_v>::type> +constexpr void atom(string_builder &b, const number_type t) { + b.append(t); +} + +template + requires(std::is_class_v && !concepts::container_but_not_string && + !concepts::string_view_keyed_map && + !concepts::optional_type && + !concepts::smart_pointer && + !concepts::appendable_containers && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && !require_custom_serialization) +constexpr void atom(string_builder &b, const T &t) { + int i = 0; + b.append('{'); + template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) { + if (i != 0) + b.append(','); + constexpr auto key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(dm))); + b.append_raw(key); + b.append(':'); + atom(b, t.[:dm:]); + i++; + }; + b.append('}'); +} + +// Support for optional types (std::optional, etc.) +template + requires(!require_custom_serialization) +constexpr void atom(string_builder &b, const T &opt) { + if (opt) { + atom(b, opt.value()); + } else { + b.append_raw("null"); + } +} + +// Support for smart pointers (std::unique_ptr, std::shared_ptr, etc.) +template + requires(!require_custom_serialization) +constexpr void atom(string_builder &b, const T &ptr) { + if (ptr) { + atom(b, *ptr); + } else { + b.append_raw("null"); + } +} + +// Support for enums - serialize as string representation using expand approach from P2996R12 +template + requires(std::is_enum_v && !require_custom_serialization) +void atom(string_builder &b, const T &e) { +#if SIMDJSON_STATIC_REFLECTION + constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T)); + template for (constexpr auto enum_val : enumerators) { + constexpr auto enum_str = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(enum_val))); + if (e == [:enum_val:]) { + b.append_raw(enum_str); + return; + } + }; + // Fallback to integer if enum value not found + atom(b, static_cast>(e)); +#else + // Fallback: serialize as integer if reflection not available + atom(b, static_cast>(e)); +#endif +} + +// Support for appendable containers that don't have operator[] (sets, etc.) +template + requires(!concepts::container_but_not_string && !concepts::string_view_keyed_map && + !concepts::optional_type && !concepts::smart_pointer && + !std::is_same_v && + !std::is_same_v && !std::is_same_v && !require_custom_serialization) +constexpr void atom(string_builder &b, const T &container) { + if (container.empty()) { + b.append_raw("[]"); + return; + } + b.append('['); + bool first = true; + for (const auto& item : container) { + if (!first) { + b.append(','); + } + first = false; + atom(b, item); + } + b.append(']'); +} + +// append functions that delegate to atom functions for primitive types +template + requires(std::is_arithmetic_v && !std::is_same_v) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!concepts::container_but_not_string && !concepts::string_view_keyed_map && + !concepts::optional_type && !concepts::smart_pointer && + !std::is_same_v && + !std::is_same_v && !std::is_same_v && !require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +// works for struct +template + requires(std::is_class_v && !concepts::container_but_not_string && + !concepts::string_view_keyed_map && + !concepts::optional_type && + !concepts::smart_pointer && + !concepts::appendable_containers && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && !require_custom_serialization) +void append(string_builder &b, const Z &z) { + int i = 0; + b.append('{'); + template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^Z, std::meta::access_context::unchecked()))) { + if (i != 0) + b.append(','); + constexpr auto key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(dm))); + b.append_raw(key); + b.append(':'); + atom(b, z.[:dm:]); + i++; + }; + b.append('}'); +} + +// works for container that have begin() and end() iterators +template + requires(concepts::container_but_not_string && !require_custom_serialization) +void append(string_builder &b, const Z &z) { + auto it = z.begin(); + auto end = z.end(); + if (it == end) { + b.append_raw("[]"); + return; + } + b.append('['); + atom(b, *it); + ++it; + for (; it != end; ++it) { + b.append(','); + atom(b, *it); + } + b.append(']'); +} + +template + requires (require_custom_serialization) +void append(string_builder &b, const Z &z) { + b.append(z); +} + + +template +simdjson_warn_unused simdjson_result to_json_string(const Z &z, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) { + string_builder b(initial_capacity); + append(b, z); + std::string_view s; + if(auto e = b.view().get(s); e) { return e; } + return std::string(s); +} + +template +simdjson_warn_unused simdjson_error to_json(const Z &z, std::string &s, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) { + string_builder b(initial_capacity); + append(b, z); + std::string_view view; + if(auto e = b.view().get(view); e) { return e; } + s.assign(view); + return SUCCESS; +} + +template +string_builder& operator<<(string_builder& b, const Z& z) { + append(b, z); + return b; +} + +// extract_from: Serialize only specific fields from a struct to JSON +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +void extract_from(string_builder &b, const T &obj) { + // Helper to check if a field name matches any of the requested fields + auto should_extract = [](std::string_view field_name) constexpr -> bool { + return ((FieldNames.view() == field_name) || ...); + }; + + b.append('{'); + bool first = true; + + // Iterate through all members of T using reflection + template for (constexpr auto mem : std::define_static_array( + std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) { + + if constexpr (std::meta::is_public(mem)) { + constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem)); + + // Only serialize this field if it's in our list of requested fields + if constexpr (should_extract(key)) { + if (!first) { + b.append(','); + } + first = false; + + // Serialize the key + constexpr auto quoted_key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(mem))); + b.append_raw(quoted_key); + b.append(':'); + + // Serialize the value + atom(b, obj.[:mem:]); + } + } + }; + + b.append('}'); +} + +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_result extract_from(const T &obj, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) { + string_builder b(initial_capacity); + extract_from(b, obj); + std::string_view s; + if(auto e = b.view().get(s); e) { return e; } + return std::string(s); +} + +} // namespace builder +} // namespace icelake +// Alias the function template to 'to' in the global namespace +template +simdjson_warn_unused simdjson_result to_json(const Z &z, size_t initial_capacity = icelake::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { + icelake::builder::string_builder b(initial_capacity); + icelake::builder::append(b, z); + std::string_view s; + if(auto e = b.view().get(s); e) { return e; } + return std::string(s); +} +template +simdjson_warn_unused simdjson_error to_json(const Z &z, std::string &s, size_t initial_capacity = icelake::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { + icelake::builder::string_builder b(initial_capacity); + icelake::builder::append(b, z); + std::string_view view; + if(auto e = b.view().get(view); e) { return e; } + s.assign(view); + return SUCCESS; +} +// Global namespace function for extract_from +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_result extract_from(const T &obj, size_t initial_capacity = icelake::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { + icelake::builder::string_builder b(initial_capacity); + icelake::builder::extract_from(b, obj); + std::string_view s; + if(auto e = b.view().get(s); e) { return e; } + return std::string(s); +} + +} // namespace simdjson + +#endif // SIMDJSON_STATIC_REFLECTION + +#endif +/* end file simdjson/generic/ondemand/json_builder.h for icelake */ /* end file simdjson/generic/ondemand/amalgamated.h for icelake */ /* including simdjson/icelake/end.h: #include "simdjson/icelake/end.h" */ @@ -77742,7 +88122,7 @@ using namespace simd; struct backslash_and_quote { public: static constexpr uint32_t BYTES_PROCESSED = 32; - simdjson_inline static backslash_and_quote + simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); simdjson_inline bool has_quote_first() { @@ -77783,6 +88163,32 @@ backslash_and_quote::copy_and_find(const uint8_t *src, uint8_t *dst) { }; } + +struct escaping { + static constexpr uint32_t BYTES_PROCESSED = 16; + simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst); + + simdjson_inline bool has_escape() { return escape_bits != 0; } + simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); } + + uint64_t escape_bits; +}; // struct escaping + + + +simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) { + static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes"); + simd8 v(src); + v.store(dst); + simd8 is_quote = (v == '"'); + simd8 is_backslash = (v == '\\'); + simd8 is_control = (v < 32); + return { + // We store it as a 64-bit bitmask even though we only need 16 bits. + uint64_t((is_backslash | is_quote | is_control).to_bitmask()) + }; +} + } // unnamed namespace } // namespace ppc64 } // namespace simdjson @@ -77851,7 +88257,7 @@ class value_iterator; /* end file simdjson/generic/ondemand/base.h for ppc64 */ /* including simdjson/generic/ondemand/deserialize.h for ppc64: #include "simdjson/generic/ondemand/deserialize.h" */ /* begin file simdjson/generic/ondemand/deserialize.h for ppc64 */ -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS #ifndef SIMDJSON_ONDEMAND_DESERIALIZE_H /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ @@ -77860,55 +88266,8 @@ class value_iterator; /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ -#include namespace simdjson { -namespace tag_invoke_fn_ns { -void tag_invoke(); - -struct tag_invoke_fn { - template - requires requires(Tag tag, Args &&...args) { - tag_invoke(std::forward(tag), std::forward(args)...); - } - constexpr auto operator()(Tag tag, Args &&...args) const - noexcept(noexcept(tag_invoke(std::forward(tag), - std::forward(args)...))) - -> decltype(tag_invoke(std::forward(tag), - std::forward(args)...)) { - return tag_invoke(std::forward(tag), std::forward(args)...); - } -}; -} // namespace tag_invoke_fn_ns - -inline namespace tag_invoke_ns { -inline constexpr tag_invoke_fn_ns::tag_invoke_fn tag_invoke = {}; -} // namespace tag_invoke_ns - -template -concept tag_invocable = requires(Tag tag, Args... args) { - tag_invoke(std::forward(tag), std::forward(args)...); -}; - -template -concept nothrow_tag_invocable = - tag_invocable && requires(Tag tag, Args... args) { - { - tag_invoke(std::forward(tag), std::forward(args)...) - } noexcept; - }; - -template -using tag_invoke_result = - std::invoke_result; - -template -using tag_invoke_result_t = - std::invoke_result_t; - -template using tag_t = std::decay_t; - - struct deserialize_tag; /// These types are deserializable in a built-in way @@ -77930,7 +88289,7 @@ template concept custom_deserializable = tag_invocable; template -concept deserializable = custom_deserializable || is_builtin_deserializable_v; +concept deserializable = custom_deserializable || is_builtin_deserializable_v || concepts::optional_type; template concept nothrow_custom_deserializable = nothrow_tag_invocable; @@ -77941,28 +88300,44 @@ concept nothrow_deserializable = nothrow_custom_deserializable || is_bu /// Deserialize Tag inline constexpr struct deserialize_tag { + using array_type = ppc64::ondemand::array; + using object_type = ppc64::ondemand::object; using value_type = ppc64::ondemand::value; using document_type = ppc64::ondemand::document; using document_reference_type = ppc64::ondemand::document_reference; + // Customization Point for array + template + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(array_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for object + template + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(object_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + // Customization Point for value template requires custom_deserializable - [[nodiscard]] constexpr /* error_code */ auto operator()(value_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + simdjson_warn_unused constexpr /* error_code */ auto operator()(value_type &object, T& output) const noexcept(nothrow_custom_deserializable) { return tag_invoke(*this, object, output); } // Customization Point for document template requires custom_deserializable - [[nodiscard]] constexpr /* error_code */ auto operator()(document_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + simdjson_warn_unused constexpr /* error_code */ auto operator()(document_type &object, T& output) const noexcept(nothrow_custom_deserializable) { return tag_invoke(*this, object, output); } // Customization Point for document reference template requires custom_deserializable - [[nodiscard]] constexpr /* error_code */ auto operator()(document_reference_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + simdjson_warn_unused constexpr /* error_code */ auto operator()(document_reference_type &object, T& output) const noexcept(nothrow_custom_deserializable) { return tag_invoke(*this, object, output); } @@ -77972,7 +88347,7 @@ inline constexpr struct deserialize_tag { } // namespace simdjson #endif // SIMDJSON_ONDEMAND_DESERIALIZE_H -#endif // SIMDJSON_SUPPORTS_DESERIALIZATION +#endif // SIMDJSON_SUPPORTS_CONCEPTS /* end file simdjson/generic/ondemand/deserialize.h for ppc64 */ /* including simdjson/generic/ondemand/value_iterator.h for ppc64: #include "simdjson/generic/ondemand/value_iterator.h" */ @@ -78314,7 +88689,7 @@ class value_iterator { simdjson_warn_unused simdjson_inline simdjson_result get_root_number(bool check_trailing) noexcept; simdjson_warn_unused simdjson_inline simdjson_result is_root_null(bool check_trailing) noexcept; - simdjson_inline error_code error() const noexcept; + simdjson_warn_unused simdjson_inline error_code error() const noexcept; simdjson_inline uint8_t *&string_buf_loc() noexcept; simdjson_inline const json_iterator &json_iter() const noexcept; simdjson_inline json_iterator &json_iter() noexcept; @@ -78398,8 +88773,8 @@ class value_iterator { simdjson_inline const uint8_t *peek_non_root_scalar(const char *type) noexcept; - simdjson_inline error_code start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept; - simdjson_inline error_code end_container() noexcept; + simdjson_warn_unused simdjson_inline error_code start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept; + simdjson_warn_unused simdjson_inline error_code end_container() noexcept; /** * Advance to a place expecting a value (increasing depth). @@ -78409,8 +88784,8 @@ class value_iterator { */ simdjson_inline simdjson_result advance_to_value() noexcept; - simdjson_inline error_code incorrect_type_error(const char *message) const noexcept; - simdjson_inline error_code error_unless_more_tokens(uint32_t tokens=1) const noexcept; + simdjson_warn_unused simdjson_inline error_code incorrect_type_error(const char *message) const noexcept; + simdjson_warn_unused simdjson_inline error_code error_unless_more_tokens(uint32_t tokens=1) const noexcept; simdjson_inline bool is_at_start() const noexcept; /** @@ -78447,7 +88822,7 @@ class value_iterator { /** @copydoc error_code json_iterator::end_position() const noexcept; */ simdjson_inline token_position end_position() const noexcept; /** @copydoc error_code json_iterator::report_error(error_code error, const char *message) noexcept; */ - simdjson_inline error_code report_error(error_code error, const char *message) noexcept; + simdjson_warn_unused simdjson_inline error_code report_error(error_code error, const char *message) noexcept; friend class document; friend class object; @@ -78512,13 +88887,14 @@ class value { * * You may use get_double(), get_bool(), get_uint64(), get_int64(), * get_object(), get_array(), get_raw_json_string(), or get_string() instead. + * When SIMDJSON_SUPPORTS_CONCEPTS is set, custom types are also supported. * * @returns A value of the given type, parsed from the JSON. * @returns INCORRECT_TYPE If the JSON value is not the given type. */ template simdjson_inline simdjson_result get() -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept @@ -78535,22 +88911,38 @@ class value { * Get this value as the given type. * * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool + * If the macro SIMDJSON_SUPPORTS_CONCEPTS is set, then custom types are also supported. * * @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized. * @returns INCORRECT_TYPE If the JSON value is not an object. * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. */ template - simdjson_inline error_code get(T &out) -#if SIMDJSON_SUPPORTS_DESERIALIZATION + simdjson_warn_unused simdjson_inline error_code get(T &out) +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept #endif { - #if SIMDJSON_SUPPORTS_DESERIALIZATION + #if SIMDJSON_SUPPORTS_CONCEPTS if constexpr (custom_deserializable) { return deserialize(*this, out); + } else if constexpr (concepts::optional_type) { + using value_type = typename std::remove_cvref_t::value_type; + + // Check if the value is null + bool is_null_value; + SIMDJSON_TRY( is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); // Set to nullopt + return SUCCESS; + } + + if (!out) { + out.emplace(); + } + return get(out.value()); } else { static_assert(!sizeof(T), "The get method with type T is not implemented by the simdjson library. " "And you do not seem to have added support for it. Indeed, we have that " @@ -78560,7 +88952,7 @@ class value { static_cast(out); // to get rid of unused errors return UNINITIALIZED; } -#else // SIMDJSON_SUPPORTS_DESERIALIZATION +#else // SIMDJSON_SUPPORTS_CONCEPTS // Unless the simdjson library or the user provides an inline implementation, calling this method should // immediately fail. static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " @@ -78678,7 +89070,7 @@ class value { * @returns INCORRECT_TYPE if the JSON value is not a string. Otherwise, we return SUCCESS. */ template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; /** * Cast this JSON value to a "wobbly" string. @@ -79199,7 +89591,7 @@ struct simdjson_result : public ppc64::implementation_si simdjson_inline simdjson_result get_double_in_string() noexcept; simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; simdjson_inline simdjson_result get_wobbly_string() noexcept; simdjson_inline simdjson_result get_raw_json_string() noexcept; simdjson_inline simdjson_result get_bool() noexcept; @@ -79280,7 +89672,22 @@ struct simdjson_result : public ppc64::implementation_si simdjson_result operator[](int) noexcept = delete; /** - * Get the type of this JSON value. + * Get the type of this JSON value. It does not validate or consume the value. + * E.g., you must still call "is_null()" to check that a value is null even if + * "type()" returns json_type::null. + * + * Given a valid JSON document, the answer can be one of + * simdjson::ondemand::json_type::object, + * simdjson::ondemand::json_type::array, + * simdjson::ondemand::json_type::string, + * simdjson::ondemand::json_type::number, + * simdjson::ondemand::json_type::boolean, + * simdjson::ondemand::json_type::null. + * + * Starting with simdjson 4.0, this function will return simdjson::ondemand::json_type::unknown + * given a bad token. + * This allows you to identify a case such as {"key": NaN} and identify the NaN value. + * The simdjson::ondemand::json_type::unknown value should only happen with non-valid JSON. * * NOTE: If you're only expecting a value to be one type (a typical case), it's generally * better to just call .get_double, .get_string, etc. and check for INCORRECT_TYPE (or just @@ -79774,14 +90181,14 @@ class json_iterator { * @param error The error to report. Must not be SUCCESS, UNINITIALIZED, INCORRECT_TYPE, or NO_SUCH_FIELD. * @param message An error message to report with the error. */ - simdjson_inline error_code report_error(error_code error, const char *message) noexcept; + simdjson_warn_unused simdjson_inline error_code report_error(error_code error, const char *message) noexcept; /** * Log error, but don't stop iteration. * @param error The error to report. Must be INCORRECT_TYPE, or NO_SUCH_FIELD. * @param message An error message to report with the error. */ - simdjson_inline error_code optional_error(error_code error, const char *message) noexcept; + simdjson_warn_unused simdjson_inline error_code optional_error(error_code error, const char *message) noexcept; /** * Take an input in json containing max_len characters and attempt to copy it over to tmpbuf, a buffer with @@ -79801,7 +90208,7 @@ class json_iterator { simdjson_inline void reenter_child(token_position position, depth_t child_depth) noexcept; - simdjson_inline error_code consume_character(char c) noexcept; + simdjson_warn_unused simdjson_inline error_code consume_character(char c) noexcept; #if SIMDJSON_DEVELOPMENT_CHECKS simdjson_inline token_position start_position(depth_t depth) const noexcept; simdjson_inline void set_start_position(depth_t depth, token_position position) noexcept; @@ -79892,6 +90299,7 @@ namespace ondemand { * The type of a JSON value. */ enum class json_type { + unknown=0, // Start at 1 to catch uninitialized / default values more easily array=1, ///< A JSON array ( [ 1, 2, 3 ... ] ) object, ///< A JSON object ( { "a": 1, "b" 2, ... } ) @@ -80098,6 +90506,12 @@ class raw_json_string { */ simdjson_inline const char * raw() const noexcept; + /** + * Get the character at index i. This is unchecked. + * [0] when the string is of length 0 returns the final quote ("). + */ + simdjson_inline char operator[](size_t i) const noexcept; + /** * This compares the current instance to the std::string_view target: returns true if * they are byte-by-byte equal (no escaping is done) on target.size() characters, @@ -80237,10 +90651,10 @@ struct simdjson_result : public ppc64::impleme simdjson_inline ~simdjson_result() noexcept = default; ///< @private simdjson_inline simdjson_result raw() const noexcept; + simdjson_inline char operator[](size_t) const noexcept; simdjson_inline simdjson_warn_unused simdjson_result unescape(ppc64::ondemand::json_iterator &iter, bool allow_replacement) const noexcept; simdjson_inline simdjson_warn_unused simdjson_result unescape_wobbly(ppc64::ondemand::json_iterator &iter) const noexcept; }; - } // namespace simdjson #endif // SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_H @@ -80256,6 +90670,7 @@ struct simdjson_result : public ppc64::impleme /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ #include +#include namespace simdjson { namespace ppc64 { @@ -80374,7 +90789,9 @@ class parser { simdjson_warn_unused simdjson_result iterate(std::string_view json, size_t capacity) & noexcept; /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ simdjson_warn_unused simdjson_result iterate(const std::string &json) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + /** @overload simdjson_result iterate(padded_string_view json) & noexcept + The string instance might be have its capacity extended. Note that this can still + result in AddressSanitizer: container-overflow in some cases. */ simdjson_warn_unused simdjson_result iterate(std::string &json) & noexcept; /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ simdjson_warn_unused simdjson_result iterate(const simdjson_result &json) & noexcept; @@ -80462,6 +90879,11 @@ class parser { * Setting batch_size to excessively large or excessively small values may impact negatively the * performance. * + * ### Threads + * + * When compiled with SIMDJSON_THREADS_ENABLED, this method will use a single thread under the + * hood to do some lookahead. + * * ### REQUIRED: Buffer Padding * * The buffer must have at least SIMDJSON_PADDING extra allocated bytes. It does not matter what @@ -80469,10 +90891,10 @@ class parser { * using a sanitizer that verifies that no uninitialized byte is read, then you should initialize the * SIMDJSON_PADDING bytes to avoid runtime warnings. * - * ### Threads + * This is checked automatically with all iterate_many function calls, except for the two + * that take pointers (const char* or const uint8_t*). * - * When compiled with SIMDJSON_THREADS_ENABLED, this method will use a single thread under the - * hood to do some lookahead. + * ### Threads * * ### Parser Capacity * @@ -80498,14 +90920,16 @@ class parser { */ inline simdjson_result iterate_many(const uint8_t *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ + inline simdjson_result iterate_many(padded_string_view json, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ inline simdjson_result iterate_many(const char *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ inline simdjson_result iterate_many(const std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; - inline simdjson_result iterate_many(const std::string &&s, size_t batch_size, bool allow_comma_separated = false) = delete;// unsafe + /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) + the string might be automatically padded with up to SIMDJSON_PADDING whitespace characters */ + inline simdjson_result iterate_many(std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ inline simdjson_result iterate_many(const padded_string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; - inline simdjson_result iterate_many(const padded_string &&s, size_t batch_size, bool allow_comma_separated = false) = delete;// unsafe - /** @private We do not want to allow implicit conversion from C string to std::string. */ simdjson_result iterate_many(const char *buf, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept = delete; @@ -80607,13 +91031,39 @@ class parser { bool string_buffer_overflow(const uint8_t *string_buf_loc) const noexcept; #endif + /** + * Get a unique parser instance corresponding to the current thread. + * This instance can be safely used within the current thread, but it should + * not be passed to other threads. + * + * A parser should only be used for one document at a time. + * + * Our simdjson::from functions use this parser instance. + * + * You can free the related parser by calling release_parser(). + */ + static simdjson_inline simdjson_warn_unused ondemand::parser& get_parser(); + /** + * Release the parser instance initialized by get_parser() and all the + * associated resources (memory). Returns true if a parser instance + * was released. + */ + static simdjson_inline bool release_parser(); + private: + friend bool release_parser(); + friend ondemand::parser& get_parser(); + /** Get the thread-local parser instance, allocates it if needed */ + static simdjson_inline simdjson_warn_unused std::unique_ptr& get_parser_instance(); + /** Get the thread-local parser instance, it might be null */ + static simdjson_inline simdjson_warn_unused std::unique_ptr& get_threadlocal_parser_if_exists(); /** @private [for benchmarking access] The implementation to use */ std::unique_ptr implementation{}; size_t _capacity{0}; size_t _max_capacity; size_t _max_depth{DEFAULT_MAX_DEPTH}; std::unique_ptr string_buf{}; + #if SIMDJSON_DEVELOPMENT_CHECKS std::unique_ptr start_positions{}; #endif @@ -80641,6 +91091,315 @@ struct simdjson_result : public ppc64::implementation_s #endif // SIMDJSON_GENERIC_ONDEMAND_PARSER_H /* end file simdjson/generic/ondemand/parser.h for ppc64 */ +// JSON builder - needed for extract_into functionality +/* including simdjson/generic/ondemand/json_string_builder.h for ppc64: #include "simdjson/generic/ondemand/json_string_builder.h" */ +/* begin file simdjson/generic/ondemand/json_string_builder.h for ppc64 */ +/** + * This file is part of the builder API. It is temporarily in the ondemand directory + * but we will move it to a builder directory later. + */ +#ifndef SIMDJSON_GENERIC_STRING_BUILDER_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { + + +#if SIMDJSON_SUPPORTS_CONCEPTS + +namespace ppc64 { +namespace builder { + class string_builder; +}} + +template +struct has_custom_serialization : std::false_type {}; + +inline constexpr struct serialize_tag { + template + requires custom_deserializable + constexpr void operator()(ppc64::builder::string_builder& b, T& obj) const{ + return tag_invoke(*this, b, obj); + } + + +} serialize{}; +template +struct has_custom_serialization(), std::declval())) +>> : std::true_type {}; + +template +constexpr bool require_custom_serialization = has_custom_serialization::value; +#else +struct has_custom_serialization : std::false_type {}; +#endif // SIMDJSON_SUPPORTS_CONCEPTS + +namespace ppc64 { +namespace builder { +/** + * A builder for JSON strings representing documents. This is a low-level + * builder that is not meant to be used directly by end-users. Though it + * supports atomic types (Booleans, strings), it does not support composed + * types (arrays and objects). + * + * Ultimately, this class can support kernel-specific optimizations. E.g., + * it may make use of SIMD instructions to escape strings faster. + */ +class string_builder { +public: + simdjson_inline string_builder(size_t initial_capacity = DEFAULT_INITIAL_CAPACITY); + + static constexpr size_t DEFAULT_INITIAL_CAPACITY = 1024; + + /** + * Append number (includes Booleans). Booleans are mapped to the strings + * false and true. Numbers are converted to strings abiding by the JSON standard. + * Floating-point numbers are converted to the shortest string that 'correctly' + * represents the number. + */ + template::value>::type> + simdjson_inline void append(number_type v) noexcept; + + /** + * Append character c. + */ + simdjson_inline void append(char c) noexcept; + + /** + * Append the string 'null'. + */ + simdjson_inline void append_null() noexcept; + + /** + * Clear the content. + */ + simdjson_inline void clear() noexcept; + + /** + * Append the std::string_view, after escaping it. + * There is no UTF-8 validation. + */ + simdjson_inline void escape_and_append(std::string_view input) noexcept; + + /** + * Append the std::string_view surrounded by double quotes, after escaping it. + * There is no UTF-8 validation. + */ + simdjson_inline void escape_and_append_with_quotes(std::string_view input) noexcept; +#if SIMDJSON_SUPPORTS_CONCEPTS + template + simdjson_inline void escape_and_append_with_quotes() noexcept; +#endif + /** + * Append the character surrounded by double quotes, after escaping it. + * There is no UTF-8 validation. + */ + simdjson_inline void escape_and_append_with_quotes(char input) noexcept; + + /** + * Append the character surrounded by double quotes, after escaping it. + * There is no UTF-8 validation. + */ + simdjson_inline void escape_and_append_with_quotes(const char* input) noexcept; + + /** + * Append the C string directly, without escaping. + * There is no UTF-8 validation. + */ + simdjson_inline void append_raw(const char *c) noexcept; + + /** + * Append "{" to the buffer. + */ + simdjson_inline void start_object() noexcept; + + /** + * Append "}" to the buffer. + */ + simdjson_inline void end_object() noexcept; + + /** + * Append "[" to the buffer. + */ + simdjson_inline void start_array() noexcept; + + /** + * Append "]" to the buffer. + */ + simdjson_inline void end_array() noexcept; + + /** + * Append "," to the buffer. + */ + simdjson_inline void append_comma() noexcept; + + /** + * Append ":" to the buffer. + */ + simdjson_inline void append_colon() noexcept; + + /** + * Append a key-value pair to the buffer. + * The key is escaped and surrounded by double quotes. + * The value is escaped if it is a string. + */ + template + simdjson_inline void append_key_value(key_type key, value_type value) noexcept; +#if SIMDJSON_SUPPORTS_CONCEPTS + template + simdjson_inline void append_key_value(value_type value) noexcept; + + // Support for optional types (std::optional, etc.) + template + requires(!require_custom_serialization) + simdjson_inline void append(const T &opt); + + template + requires(require_custom_serialization) + simdjson_inline void append(const T &val); + + // Support for string-like types + template + requires(std::is_convertible::value || + std::is_same::value ) + simdjson_inline void append(const T &value); +#endif +#if SIMDJSON_SUPPORTS_RANGES && SIMDJSON_SUPPORTS_CONCEPTS + // Support for range-based appending (std::ranges::view, etc.) + template +requires (!std::is_convertible::value) + simdjson_inline void append(const R &range) noexcept; +#endif + /** + * Append the std::string_view directly, without escaping. + * There is no UTF-8 validation. + */ + simdjson_inline void append_raw(std::string_view input) noexcept; + + /** + * Append len characters from str. + * There is no UTF-8 validation. + */ + simdjson_inline void append_raw(const char *str, size_t len) noexcept; +#if SIMDJSON_EXCEPTIONS + /** + * Creates an std::string from the written JSON buffer. + * Throws if memory allocation failed + * + * The result may not be valid UTF-8 if some of your content was not valid UTF-8. + * Use validate_unicode() to check the content if needed. + */ + simdjson_inline operator std::string() const noexcept(false); + + /** + * Creates an std::string_view from the written JSON buffer. + * Throws if memory allocation failed. + * + * The result may not be valid UTF-8 if some of your content was not valid UTF-8. + * Use validate_unicode() to check the content if needed. + */ + simdjson_inline operator std::string_view() const noexcept(false) simdjson_lifetime_bound; +#endif + + /** + * Returns a view on the written JSON buffer. Returns an error + * if memory allocation failed. + * + * The result may not be valid UTF-8 if some of your content was not valid UTF-8. + * Use validate_unicode() to check the content. + */ + simdjson_inline simdjson_result view() const noexcept; + + /** + * Appends the null character to the buffer and returns + * a pointer to the beginning of the written JSON buffer. + * Returns an error if memory allocation failed. + * The result is null-terminated. + * + * The result may not be valid UTF-8 if some of your content was not valid UTF-8. + * Use validate_unicode() to check the content. + */ + simdjson_inline simdjson_result c_str() noexcept; + + /** + * Return true if the content is valid UTF-8. + */ + simdjson_inline bool validate_unicode() const noexcept; + + /** + * Returns the current size of the written JSON buffer. + * If an error occurred, returns 0. + */ + simdjson_inline size_t size() const noexcept; + +private: + /** + * Returns true if we can write at least upcoming_bytes bytes. + * The underlying buffer is reallocated if needed. It is designed + * to be called before writing to the buffer. It should be fast. + */ + simdjson_inline bool capacity_check(size_t upcoming_bytes); + + /** + * Grow the buffer to at least desired_capacity bytes. + * If the allocation fails, is_valid is set to false. We expect + * that this function would not be repeatedly called. + */ + simdjson_inline void grow_buffer(size_t desired_capacity); + + /** + * We use this helper function to make sure that is_valid is kept consistent. + */ + simdjson_inline void set_valid(bool valid) noexcept; + + std::unique_ptr buffer{}; + size_t position{0}; + size_t capacity{0}; + bool is_valid{true}; +}; + + + +} +} + + +#if !SIMDJSON_STATIC_REFLECTION +// fallback implementation until we have static reflection +template +simdjson_warn_unused simdjson_result to_json(const Z &z, size_t initial_capacity = simdjson::ppc64::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { + simdjson::ppc64::builder::string_builder b(initial_capacity); + b.append(z); + std::string_view s; + auto e = b.view().get(s); + if(e) { return e; } + return std::string(s); +} +template +simdjson_warn_unused simdjson_error to_json(const Z &z, std::string &s, size_t initial_capacity = simdjson::ppc64::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { + simdjson::ppc64::builder::string_builder b(initial_capacity); + b.append(z); + std::string_view sv; + auto e = b.view().get(sv); + if(e) { return e; } + s.assign(sv.data(), sv.size()); + return simdjson::SUCCESS; +} +#endif + +#if SIMDJSON_SUPPORTS_CONCEPTS +#endif // SIMDJSON_SUPPORTS_CONCEPTS + +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_STRING_BUILDER_H +/* end file simdjson/generic/ondemand/json_string_builder.h for ppc64 */ + // All other declarations /* including simdjson/generic/ondemand/array.h for ppc64: #include "simdjson/generic/ondemand/array.h" */ /* begin file simdjson/generic/ondemand/array.h for ppc64 */ @@ -80777,11 +91536,42 @@ class array { * - INDEX_OUT_OF_BOUNDS if the array index is larger than an array length */ simdjson_inline simdjson_result at(size_t index) noexcept; + +#if SIMDJSON_SUPPORTS_CONCEPTS + /** + * Get this array as the given type. + * + * @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized. + * @returns INCORRECT_TYPE If the JSON array is not of the given type. + * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. + */ + template + simdjson_warn_unused simdjson_inline error_code get(T &out) + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) { + static_assert(custom_deserializable); + return deserialize(*this, out); + } + /** + * Get this array as the given type. + * + * @returns A value of the given type, parsed from the JSON. + * @returns INCORRECT_TYPE If the JSON value is not the given type. + */ + template + simdjson_inline simdjson_result get() + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) + { + static_assert(std::is_default_constructible::value, "The specified type is not default constructible."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } +#endif // SIMDJSON_SUPPORTS_CONCEPTS protected: /** * Go to the end of the array, no matter where you are right now. */ - simdjson_inline error_code consume() noexcept; + simdjson_warn_unused simdjson_inline error_code consume() noexcept; /** * Begin array iteration. @@ -80855,7 +91645,28 @@ struct simdjson_result : public ppc64::implementation_si simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; simdjson_inline simdjson_result raw_json() noexcept; +#if SIMDJSON_SUPPORTS_CONCEPTS + // TODO: move this code into object-inl.h + template + simdjson_inline simdjson_result get() noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + return first; + } + return first.get(); + } + template + simdjson_warn_unused simdjson_inline error_code get(T& out) noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + out = first; + } else { + SIMDJSON_TRY( first.get(out) ); + } + return SUCCESS; + } +#endif // SIMDJSON_SUPPORTS_CONCEPTS }; } // namespace simdjson @@ -80900,7 +91711,8 @@ class array_iterator { * * Part of the std::iterator interface. */ - simdjson_inline simdjson_result operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. + simdjson_inline simdjson_result + operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. /** * Check if we are at the end of the JSON. * @@ -80924,6 +91736,11 @@ class array_iterator { */ simdjson_inline array_iterator &operator++() noexcept; + /** + * Check if the array is at the end. + */ + simdjson_warn_unused simdjson_inline bool at_end() const noexcept; + private: value_iterator iter{}; @@ -80942,7 +91759,6 @@ namespace simdjson { template<> struct simdjson_result : public ppc64::implementation_simdjson_result_base { -public: simdjson_inline simdjson_result(ppc64::ondemand::array_iterator &&value) noexcept; ///< @private simdjson_inline simdjson_result(error_code error) noexcept; ///< @private simdjson_inline simdjson_result() noexcept = default; @@ -80955,6 +91771,8 @@ struct simdjson_result : public ppc64::implemen simdjson_inline bool operator==(const simdjson_result &) const noexcept; simdjson_inline bool operator!=(const simdjson_result &) const noexcept; simdjson_inline simdjson_result &operator++() noexcept; + + simdjson_warn_unused simdjson_inline bool at_end() const noexcept; }; } // namespace simdjson @@ -81082,7 +91900,7 @@ class document { * @returns INCORRECT_TYPE if the JSON value is not a string. Otherwise, we return SUCCESS. */ template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; /** * Cast this JSON value to a string. * @@ -81148,7 +91966,7 @@ class document { */ template simdjson_inline simdjson_result get() & -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept @@ -81171,7 +91989,7 @@ class document { */ template simdjson_inline simdjson_result get() && -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept @@ -81189,18 +92007,18 @@ class document { * Be mindful that the document instance must remain in scope while you are accessing object, array and value instances. * * @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized. - * @returns INCORRECT_TYPE If the JSON value is not an object. + * @returns INCORRECT_TYPE If the JSON value is of the given type. * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. */ template - simdjson_inline error_code get(T &out) & -#if SIMDJSON_SUPPORTS_DESERIALIZATION + simdjson_warn_unused simdjson_inline error_code get(T &out) & +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept #endif { -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS if constexpr (custom_deserializable) { return deserialize(*this, out); } else { @@ -81212,7 +92030,7 @@ class document { static_cast(out); // to get rid of unused errors return UNINITIALIZED; } -#else // SIMDJSON_SUPPORTS_DESERIALIZATION +#else // SIMDJSON_SUPPORTS_CONCEPTS // Unless the simdjson library or the user provides an inline implementation, calling this method should // immediately fail. static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " @@ -81222,7 +92040,7 @@ class document { " You may also add support for custom types, see our documentation."); static_cast(out); // to get rid of unused errors return UNINITIALIZED; -#endif // SIMDJSON_SUPPORTS_DESERIALIZATION +#endif // SIMDJSON_SUPPORTS_CONCEPTS } /** @overload template error_code get(T &out) & noexcept */ @@ -81287,7 +92105,7 @@ class document { * time it parses a document or when it is destroyed. * @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string. */ - simdjson_inline operator std::string_view() noexcept(false); + simdjson_inline operator std::string_view() noexcept(false) simdjson_lifetime_bound; /** * Cast this JSON value to a raw_json_string. * @@ -81296,7 +92114,7 @@ class document { * @returns A pointer to the raw JSON for the given string. * @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string. */ - simdjson_inline operator raw_json_string() noexcept(false); + simdjson_inline operator raw_json_string() noexcept(false) simdjson_lifetime_bound; /** * Cast this JSON value to a bool. * @@ -81446,11 +92264,27 @@ class document { * E.g., you must still call "is_null()" to check that a value is null even if * "type()" returns json_type::null. * + * The answer can be one of + * simdjson::ondemand::json_type::object, + * simdjson::ondemand::json_type::array, + * simdjson::ondemand::json_type::string, + * simdjson::ondemand::json_type::number, + * simdjson::ondemand::json_type::boolean, + * simdjson::ondemand::json_type::null. + * + * Starting with simdjson 4.0, this function will return simdjson::ondemand::json_type::unknown + * given a bad token. + * This allows you to identify a case such as {"key": NaN} and identify the NaN value. + * The simdjson::ondemand::json_type::unknown value should only happen with non-valid JSON. + * * NOTE: If you're only expecting a value to be one type (a typical case), it's generally * better to just call .get_double, .get_string, etc. and check for INCORRECT_TYPE (or just * let it throw an exception). * - * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". + * Prior to simdjson 4.0, this function would return an error given a bad token. + * Starting with simdjson 4.0, it will return simdjson::ondemand::json_type::unknown. + * This allows you to identify a case such as {"key": NaN} and identify the NaN value. + * The simdjson::ondemand::json_type::unknown value should only happen with non-valid JSON. */ simdjson_inline simdjson_result type() noexcept; @@ -81674,11 +92508,41 @@ class document { * the JSON document. */ simdjson_inline simdjson_result raw_json() noexcept; + +#if SIMDJSON_STATIC_REFLECTION + /** + * Extract only specific fields from the JSON object into a struct. + * + * This allows selective deserialization of only the fields you need, + * potentially improving performance by skipping unwanted fields. + * + * Example: + * ```c++ + * struct Car { + * std::string make; + * std::string model; + * int year; + * double price; + * }; + * + * Car car; + * doc.extract_into<"make", "model">(car); + * // Only 'make' and 'model' fields are extracted from JSON + * ``` + * + * @tparam FieldNames Compile-time string literals specifying which fields to extract + * @param out The output struct to populate with selected fields + * @returns SUCCESS on success, or an error code if a required field is missing or has wrong type + */ + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept; +#endif // SIMDJSON_STATIC_REFLECTION protected: /** * Consumes the document. */ - simdjson_inline error_code consume() noexcept; + simdjson_warn_unused simdjson_inline error_code consume() noexcept; simdjson_inline document(ondemand::json_iterator &&iter) noexcept; simdjson_inline const uint8_t *text(uint32_t idx) const noexcept; @@ -81731,7 +92595,7 @@ class document_reference { simdjson_inline simdjson_result get_double_in_string() noexcept; simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; simdjson_inline simdjson_result get_wobbly_string() noexcept; simdjson_inline simdjson_result get_raw_json_string() noexcept; simdjson_inline simdjson_result get_bool() noexcept; @@ -81740,7 +92604,7 @@ class document_reference { simdjson_inline simdjson_result is_null() noexcept; template simdjson_inline simdjson_result get() & -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept @@ -81753,7 +92617,7 @@ class document_reference { } template simdjson_inline simdjson_result get() && -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept @@ -81775,14 +92639,14 @@ class document_reference { * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. */ template - simdjson_inline error_code get(T &out) & -#if SIMDJSON_SUPPORTS_DESERIALIZATION + simdjson_warn_unused simdjson_inline error_code get(T &out) & +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept #endif { -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS if constexpr (custom_deserializable) { return deserialize(*this, out); } else { @@ -81794,7 +92658,7 @@ class document_reference { static_cast(out); // to get rid of unused errors return UNINITIALIZED; } -#else // SIMDJSON_SUPPORTS_DESERIALIZATION +#else // SIMDJSON_SUPPORTS_CONCEPTS // Unless the simdjson library or the user provides an inline implementation, calling this method should // immediately fail. static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " @@ -81804,12 +92668,17 @@ class document_reference { " You may also add support for custom types, see our documentation."); static_cast(out); // to get rid of unused errors return UNINITIALIZED; -#endif // SIMDJSON_SUPPORTS_DESERIALIZATION +#endif // SIMDJSON_SUPPORTS_CONCEPTS } /** @overload template error_code get(T &out) & noexcept */ template simdjson_inline error_code get(T &out) && noexcept; simdjson_inline simdjson_result raw_json() noexcept; +#if SIMDJSON_STATIC_REFLECTION + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept; +#endif // SIMDJSON_STATIC_REFLECTION simdjson_inline operator document&() const noexcept; #if SIMDJSON_EXCEPTIONS template @@ -81878,7 +92747,7 @@ struct simdjson_result : public ppc64::implementation simdjson_inline simdjson_result get_double_in_string() noexcept; simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; simdjson_inline simdjson_result get_wobbly_string() noexcept; simdjson_inline simdjson_result get_raw_json_string() noexcept; simdjson_inline simdjson_result get_bool() noexcept; @@ -81891,6 +92760,9 @@ struct simdjson_result : public ppc64::implementation template simdjson_inline error_code get(T &out) & noexcept; template simdjson_inline error_code get(T &out) && noexcept; #if SIMDJSON_EXCEPTIONS + + using ppc64::implementation_simdjson_result_base::operator*; + using ppc64::implementation_simdjson_result_base::operator->; template ::value == false>::type> explicit simdjson_inline operator T() noexcept(false); simdjson_inline operator ppc64::ondemand::array() & noexcept(false); @@ -81930,6 +92802,11 @@ struct simdjson_result : public ppc64::implementation simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; +#if SIMDJSON_STATIC_REFLECTION + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept; +#endif // SIMDJSON_STATIC_REFLECTION }; @@ -81956,7 +92833,7 @@ struct simdjson_result : public ppc64::impl simdjson_inline simdjson_result get_double_in_string() noexcept; simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; simdjson_inline simdjson_result get_wobbly_string() noexcept; simdjson_inline simdjson_result get_raw_json_string() noexcept; simdjson_inline simdjson_result get_bool() noexcept; @@ -82007,6 +92884,11 @@ struct simdjson_result : public ppc64::impl simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; +#if SIMDJSON_STATIC_REFLECTION + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept; +#endif // SIMDJSON_STATIC_REFLECTION }; @@ -82149,6 +93031,7 @@ class document_stream { * Default constructor. */ simdjson_inline iterator() noexcept; + simdjson_inline iterator(const iterator &other) noexcept = default; /** * Get the current document (or error). */ @@ -82162,6 +93045,7 @@ class document_stream { * @param other the end iterator to compare to. */ simdjson_inline bool operator!=(const iterator &other) const noexcept; + simdjson_inline bool operator==(const iterator &other) const noexcept; /** * @private * @@ -82205,6 +93089,11 @@ class document_stream { */ inline error_code error() const noexcept; + /** + * Returns whether the iterator is at the end. + */ + inline bool at_end() const noexcept; + private: simdjson_inline iterator(document_stream *s, bool finished) noexcept; /** The document_stream we're iterating through. */ @@ -82216,6 +93105,7 @@ class document_stream { friend class document_stream; friend class json_iterator; }; + using iterator = document_stream::iterator; /** * Start iterating the documents in the stream. @@ -82479,6 +93369,9 @@ struct simdjson_result : public ppc64::implementation_si /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */ +/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION && SIMDJSON_SUPPORTS_CONCEPTS */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_string_builder.h" // for constevalutil::fixed_string */ +/* amalgamation skipped (editor-only): #endif */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { @@ -82676,11 +93569,71 @@ class object { */ simdjson_inline simdjson_result raw_json() noexcept; +#if SIMDJSON_SUPPORTS_CONCEPTS + /** + * Get this object as the given type. + * + * @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized. + * @returns INCORRECT_TYPE If the JSON object is not of the given type. + * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. + */ + template + simdjson_warn_unused simdjson_inline error_code get(T &out) + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) { + static_assert(custom_deserializable); + return deserialize(*this, out); + } + /** + * Get this array as the given type. + * + * @returns A value of the given type, parsed from the JSON. + * @returns INCORRECT_TYPE If the JSON value is not the given type. + */ + template + simdjson_inline simdjson_result get() + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) + { + static_assert(std::is_default_constructible::value, "The specified type is not default constructible."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } + +#if SIMDJSON_STATIC_REFLECTION + /** + * Extract only specific fields from the JSON object into a struct. + * + * This allows selective deserialization of only the fields you need, + * potentially improving performance by skipping unwanted fields. + * + * Example: + * ```c++ + * struct Car { + * std::string make; + * std::string model; + * int year; + * double price; + * }; + * + * Car car; + * object.extract_into<"make", "model">(car); + * // Only 'make' and 'model' fields are extracted from JSON + * ``` + * + * @tparam FieldNames Compile-time string literals specifying which fields to extract + * @param out The output struct to populate with selected fields + * @returns SUCCESS on success, or an error code if a required field is missing or has wrong type + */ + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept; +#endif // SIMDJSON_STATIC_REFLECTION +#endif // SIMDJSON_SUPPORTS_CONCEPTS protected: /** * Go to the end of the object, no matter where you are right now. */ - simdjson_inline error_code consume() noexcept; + simdjson_warn_unused simdjson_inline error_code consume() noexcept; static simdjson_inline simdjson_result start(value_iterator &iter) noexcept; static simdjson_inline simdjson_result start_root(value_iterator &iter) noexcept; static simdjson_inline simdjson_result started(value_iterator &iter) noexcept; @@ -82719,12 +93672,42 @@ struct simdjson_result : public ppc64::implementation_s simdjson_inline simdjson_result operator[](std::string_view key) && noexcept; simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; - inline simdjson_result reset() noexcept; inline simdjson_result is_empty() noexcept; inline simdjson_result count_fields() & noexcept; inline simdjson_result raw_json() noexcept; + #if SIMDJSON_SUPPORTS_CONCEPTS + // TODO: move this code into object-inl.h + template + simdjson_inline simdjson_result get() noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + return first; + } + return first.get(); + } + template + simdjson_warn_unused simdjson_inline error_code get(T& out) noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + out = first; + } else { + SIMDJSON_TRY( first.get(out) ); + } + return SUCCESS; + } + +#if SIMDJSON_STATIC_REFLECTION + // TODO: move this code into object-inl.h + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) noexcept { + if (error()) { return error(); } + return first.extract_into(out); + } +#endif // SIMDJSON_STATIC_REFLECTION +#endif // SIMDJSON_SUPPORTS_CONCEPTS }; } // namespace simdjson @@ -82853,6 +93836,20 @@ inline simdjson_result to_json_string(simdjson_result to_json_string(simdjson_result x); inline simdjson_result to_json_string(simdjson_result x); inline simdjson_result to_json_string(simdjson_result x); + +#if SIMDJSON_STATIC_REFLECTION +/** + * Create a JSON string from any user-defined type using static reflection. + * Only available when SIMDJSON_STATIC_REFLECTION is enabled. + */ +template + requires(!std::same_as && + !std::same_as && + !std::same_as && + !std::same_as) +inline std::string to_json_string(const T& obj); +#endif + } // namespace simdjson /** @@ -82924,28 +93921,30 @@ inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result #include +#if SIMDJSON_STATIC_REFLECTION +#include +// #include // for std::define_static_string - header not available yet +#endif namespace simdjson { -template -constexpr bool require_custom_serialization = false; ////////////////////////////// // Number deserialization ////////////////////////////// template - requires(!require_custom_serialization) error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { using limits = std::numeric_limits; @@ -82959,7 +93958,6 @@ error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { } template - requires(!require_custom_serialization) error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { double x; SIMDJSON_TRY(val.get_double().get(x)); @@ -82968,7 +93966,6 @@ error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { } template - requires(!require_custom_serialization) error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { using limits = std::numeric_limits; @@ -82981,8 +93978,23 @@ error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { return SUCCESS; } +////////////////////////////// +// String deserialization +////////////////////////////// + +// just a character! +error_code tag_invoke(deserialize_tag, auto &val, char &out) noexcept { + std::string_view x; + SIMDJSON_TRY(val.get_string().get(x)); + if(x.size() != 1) { + return INCORRECT_TYPE; + } + out = x[0]; + return SUCCESS; +} + +// any string-like type (can be constructed from std::string_view) template - requires(!require_custom_serialization) error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(std::is_nothrow_constructible_v) { std::string_view str; SIMDJSON_TRY(val.get_string().get(str)); @@ -82999,7 +94011,6 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(std::is_nothr * doc.get>(). */ template - requires(!require_custom_serialization) error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { using value_type = typename std::remove_cvref_t::value_type; static_assert( @@ -83008,9 +94019,13 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { static_assert( std::is_default_constructible_v, "The specified type inside the container must default constructible."); - ppc64::ondemand::array arr; - SIMDJSON_TRY(val.get_array().get(arr)); + if constexpr (std::is_same_v, ppc64::ondemand::array>) { + arr = val; + } else { + SIMDJSON_TRY(val.get_array().get(arr)); + } + for (auto v : arr) { if constexpr (concepts::returns_reference) { if (auto const err = v.get().get(concepts::emplace_one(out)); @@ -83041,7 +94056,6 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { * string-keyed types. */ template - requires(!require_custom_serialization) error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { using value_type = typename std::remove_cvref_t::mapped_type; static_assert( @@ -83067,7 +94081,45 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { return SUCCESS; } +template +error_code tag_invoke(deserialize_tag, ppc64::ondemand::object &obj, T &out) noexcept { + using value_type = typename std::remove_cvref_t::mapped_type; + out.clear(); + for (auto field : obj) { + std::string_view key; + SIMDJSON_TRY(field.unescaped_key().get(key)); + + ppc64::ondemand::value value_obj; + SIMDJSON_TRY(field.value().get(value_obj)); + + value_type this_value; + SIMDJSON_TRY(value_obj.get(this_value)); + out.emplace(typename T::key_type(key), std::move(this_value)); + } + return SUCCESS; +} + +template +error_code tag_invoke(deserialize_tag, ppc64::ondemand::value &val, T &out) noexcept { + ppc64::ondemand::object obj; + SIMDJSON_TRY(val.get_object().get(obj)); + return simdjson::deserialize(obj, out); +} + +template +error_code tag_invoke(deserialize_tag, ppc64::ondemand::document &doc, T &out) noexcept { + ppc64::ondemand::object obj; + SIMDJSON_TRY(doc.get_object().get(obj)); + return simdjson::deserialize(obj, out); +} + +template +error_code tag_invoke(deserialize_tag, ppc64::ondemand::document_reference &doc, T &out) noexcept { + ppc64::ondemand::object obj; + SIMDJSON_TRY(doc.get_object().get(obj)); + return simdjson::deserialize(obj, out); +} /** @@ -83085,7 +94137,6 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { * @return status of the conversion */ template - requires(!require_custom_serialization) error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deserializable::element_type, ValT>) { using element_type = typename std::remove_cvref_t::element_type; @@ -83110,17 +94161,17 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deser /** * This CPO (Customization Point Object) will help deserialize into optional types. */ -template - requires(!require_custom_serialization) -error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deserializable::value_type, ValT>) { +template +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept(nothrow_deserializable::value_type, decltype(val)>) { using value_type = typename std::remove_cvref_t::value_type; - static_assert( - deserializable, - "The specified type inside the unique_ptr must itself be deserializable"); - static_assert( - std::is_default_constructible_v, - "The specified type inside the unique_ptr must default constructible."); + // Check if the value is null + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); // Set to nullopt + return SUCCESS; + } if (!out) { out.emplace(); @@ -83129,10 +94180,329 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deser return SUCCESS; } + +#if SIMDJSON_STATIC_REFLECTION + + +template +constexpr bool user_defined_type = (std::is_class_v +&& !std::is_same_v && !std::is_same_v && !concepts::optional_type && +!concepts::appendable_containers); + + +template + requires(user_defined_type && std::is_class_v) +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept { + ppc64::ondemand::object obj; + if constexpr (std::is_same_v, ppc64::ondemand::object>) { + obj = val; + } else { + SIMDJSON_TRY(val.get_object().get(obj)); + } + template for (constexpr auto mem : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) { + if constexpr (!std::meta::is_const(mem) && std::meta::is_public(mem)) { + constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem)); + if constexpr (concepts::optional_type) { + // for optional members, it's ok if the key is missing + auto error = obj[key].get(out.[:mem:]); + if (error && error != NO_SUCH_FIELD) { + if(error == NO_SUCH_FIELD) { + out.[:mem:].reset(); + continue; + } + return error; + } + } else { + // for non-optional members, the key must be present + SIMDJSON_TRY(obj[key].get(out.[:mem:])); + } + } + }; + return simdjson::SUCCESS; +} + +// Support for enum deserialization - deserialize from string representation using expand approach from P2996R12 +template + requires(std::is_enum_v) +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept { +#if SIMDJSON_STATIC_REFLECTION + std::string_view str; + SIMDJSON_TRY(val.get_string().get(str)); + constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T)); + template for (constexpr auto enum_val : enumerators) { + if (str == std::meta::identifier_of(enum_val)) { + out = [:enum_val:]; + return SUCCESS; + } + }; + + return INCORRECT_TYPE; +#else + // Fallback: deserialize as integer if reflection not available + std::underlying_type_t int_val; + SIMDJSON_TRY(val.get(int_val)); + out = static_cast(int_val); + return SUCCESS; +#endif +} + +template + requires(user_defined_type>) +error_code tag_invoke(deserialize_tag, simdjson_value &val, std::unique_ptr &out) noexcept { + if (!out) { + out = std::make_unique(); + if (!out) { + return MEMALLOC; + } + } + if (auto err = val.get(*out)) { + out.reset(); + return err; + } + return SUCCESS; +} + +template + requires(user_defined_type>) +error_code tag_invoke(deserialize_tag, simdjson_value &val, std::shared_ptr &out) noexcept { + if (!out) { + out = std::make_shared(); + if (!out) { + return MEMALLOC; + } + } + if (auto err = val.get(*out)) { + out.reset(); + return err; + } + return SUCCESS; +} + +#endif // SIMDJSON_STATIC_REFLECTION + +//////////////////////////////////////// +// Unique pointers +//////////////////////////////////////// +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_unique(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_bool().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_unique(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_int64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_unique(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_uint64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_unique(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_double().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_unique(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_string().get(*out)); + return SUCCESS; +} + + +//////////////////////////////////////// +// Shared pointers +//////////////////////////////////////// +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_shared(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_bool().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_shared(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_int64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_shared(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_uint64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_shared(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_double().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_shared(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_string().get(*out)); + return SUCCESS; +} + + +//////////////////////////////////////// +// Explicit optional specializations +//////////////////////////////////////// + +//////////////////////////////////////// +// Explicit smart pointer specializations for string and int types +//////////////////////////////////////// +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + // Check if the value is null + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); // Set to nullptr + return SUCCESS; + } + + if (!out) { + out = std::make_unique(); + } + std::string_view str; + SIMDJSON_TRY(val.get_string().get(str)); + *out = std::string{str}; + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + // Check if the value is null + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); // Set to nullptr + return SUCCESS; + } + + if (!out) { + out = std::make_shared(); + } + std::string_view str; + SIMDJSON_TRY(val.get_string().get(str)); + *out = std::string{str}; + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + // Check if the value is null + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); // Set to nullptr + return SUCCESS; + } + + if (!out) { + out = std::make_unique(); + } + int64_t temp; + SIMDJSON_TRY(val.get_int64().get(temp)); + *out = static_cast(temp); + return SUCCESS; +} + } // namespace simdjson #endif // SIMDJSON_ONDEMAND_DESERIALIZE_H -#endif // SIMDJSON_SUPPORTS_DESERIALIZATION +#endif // SIMDJSON_SUPPORTS_CONCEPTS /* end file simdjson/generic/ondemand/std_deserialize.h for ppc64 */ // Inline definitions @@ -83225,7 +94595,7 @@ simdjson_inline simdjson_result array::begin() noexcept { simdjson_inline simdjson_result array::end() noexcept { return array_iterator(iter); } -simdjson_inline error_code array::consume() noexcept { +simdjson_warn_unused simdjson_warn_unused simdjson_inline error_code array::consume() noexcept { auto error = iter.json_iter().skip_child(iter.depth()-1); if(error) { iter.abandon(); } return error; @@ -83416,6 +94786,9 @@ simdjson_inline array_iterator &array_iterator::operator++() noexcept { return *this; } +simdjson_inline bool array_iterator::at_end() const noexcept { + return iter.at_end(); +} } // namespace ondemand } // namespace ppc64 } // namespace simdjson @@ -83452,7 +94825,9 @@ simdjson_inline simdjson_result &simdjson_resul ++(first); return *this; } - +simdjson_inline bool simdjson_result::at_end() const noexcept { + return !first.iter.is_valid() || first.at_end(); +} } // namespace simdjson #endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_INL_H @@ -83509,7 +94884,7 @@ simdjson_inline simdjson_result value::get_string(bool allow_r return iter.get_string(allow_replacement); } template -simdjson_inline error_code value::get_string(string_type& receiver, bool allow_replacement) noexcept { +simdjson_warn_unused simdjson_inline error_code value::get_string(string_type& receiver, bool allow_replacement) noexcept { return iter.get_string(receiver, allow_replacement); } simdjson_inline simdjson_result value::get_wobbly_string() noexcept { @@ -83551,15 +94926,15 @@ template<> simdjson_inline simdjson_result value::get() noexcept { retu template<> simdjson_inline simdjson_result value::get() noexcept { return get_bool(); } -template<> simdjson_inline error_code value::get(array& out) noexcept { return get_array().get(out); } -template<> simdjson_inline error_code value::get(object& out) noexcept { return get_object().get(out); } -template<> simdjson_inline error_code value::get(raw_json_string& out) noexcept { return get_raw_json_string().get(out); } -template<> simdjson_inline error_code value::get(std::string_view& out) noexcept { return get_string(false).get(out); } -template<> simdjson_inline error_code value::get(number& out) noexcept { return get_number().get(out); } -template<> simdjson_inline error_code value::get(double& out) noexcept { return get_double().get(out); } -template<> simdjson_inline error_code value::get(uint64_t& out) noexcept { return get_uint64().get(out); } -template<> simdjson_inline error_code value::get(int64_t& out) noexcept { return get_int64().get(out); } -template<> simdjson_inline error_code value::get(bool& out) noexcept { return get_bool().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(array& out) noexcept { return get_array().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(object& out) noexcept { return get_object().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(raw_json_string& out) noexcept { return get_raw_json_string().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(std::string_view& out) noexcept { return get_string(false).get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(number& out) noexcept { return get_number().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(double& out) noexcept { return get_double().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(uint64_t& out) noexcept { return get_uint64().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(int64_t& out) noexcept { return get_int64().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(bool& out) noexcept { return get_bool().get(out); } #if SIMDJSON_EXCEPTIONS template @@ -84156,7 +95531,7 @@ simdjson_inline simdjson_result document::get_string(bool allo return get_root_value_iterator().get_root_string(true, allow_replacement); } template -simdjson_inline error_code document::get_string(string_type& receiver, bool allow_replacement) noexcept { +simdjson_warn_unused simdjson_inline error_code document::get_string(string_type& receiver, bool allow_replacement) noexcept { return get_root_value_iterator().get_root_string(receiver, true, allow_replacement); } simdjson_inline simdjson_result document::get_wobbly_string() noexcept { @@ -84182,15 +95557,15 @@ template<> simdjson_inline simdjson_result document::get() & noexcept { template<> simdjson_inline simdjson_result document::get() & noexcept { return get_bool(); } template<> simdjson_inline simdjson_result document::get() & noexcept { return get_value(); } -template<> simdjson_inline error_code document::get(array& out) & noexcept { return get_array().get(out); } -template<> simdjson_inline error_code document::get(object& out) & noexcept { return get_object().get(out); } -template<> simdjson_inline error_code document::get(raw_json_string& out) & noexcept { return get_raw_json_string().get(out); } -template<> simdjson_inline error_code document::get(std::string_view& out) & noexcept { return get_string(false).get(out); } -template<> simdjson_inline error_code document::get(double& out) & noexcept { return get_double().get(out); } -template<> simdjson_inline error_code document::get(uint64_t& out) & noexcept { return get_uint64().get(out); } -template<> simdjson_inline error_code document::get(int64_t& out) & noexcept { return get_int64().get(out); } -template<> simdjson_inline error_code document::get(bool& out) & noexcept { return get_bool().get(out); } -template<> simdjson_inline error_code document::get(value& out) & noexcept { return get_value().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(array& out) & noexcept { return get_array().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(object& out) & noexcept { return get_object().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(raw_json_string& out) & noexcept { return get_raw_json_string().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(std::string_view& out) & noexcept { return get_string(false).get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(double& out) & noexcept { return get_double().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(uint64_t& out) & noexcept { return get_uint64().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(int64_t& out) & noexcept { return get_int64().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(bool& out) & noexcept { return get_bool().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(value& out) & noexcept { return get_value().get(out); } template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_raw_json_string(); } template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_string(false); } @@ -84210,8 +95585,8 @@ simdjson_inline document::operator object() & noexcept(false) { return get_objec simdjson_inline document::operator uint64_t() noexcept(false) { return get_uint64(); } simdjson_inline document::operator int64_t() noexcept(false) { return get_int64(); } simdjson_inline document::operator double() noexcept(false) { return get_double(); } -simdjson_inline document::operator std::string_view() noexcept(false) { return get_string(false); } -simdjson_inline document::operator raw_json_string() noexcept(false) { return get_raw_json_string(); } +simdjson_inline document::operator std::string_view() noexcept(false) simdjson_lifetime_bound { return get_string(false); } +simdjson_inline document::operator raw_json_string() noexcept(false) simdjson_lifetime_bound { return get_raw_json_string(); } simdjson_inline document::operator bool() noexcept(false) { return get_bool(); } simdjson_inline document::operator value() noexcept(false) { return get_value(); } @@ -84260,7 +95635,7 @@ simdjson_inline simdjson_result document::operator[](const char *key) & n return start_or_resume_object()[key]; } -simdjson_inline error_code document::consume() noexcept { +simdjson_warn_unused simdjson_inline error_code document::consume() noexcept { bool scalar = false; auto error = is_scalar().get(scalar); if(error) { return error; } @@ -84362,6 +95737,54 @@ simdjson_inline simdjson_result document::at_path(std::string_view json_p } } + + +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION + +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code document::extract_into(T& out) & noexcept { + // Helper to check if a field name matches any of the requested fields + auto should_extract = [](std::string_view field_name) constexpr -> bool { + return ((FieldNames.view() == field_name) || ...); + }; + + // Iterate through all members of T using reflection + template for (constexpr auto mem : std::define_static_array( + std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) { + + if constexpr (!std::meta::is_const(mem) && std::meta::is_public(mem)) { + constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem)); + + // Only extract this field if it's in our list of requested fields + if constexpr (should_extract(key)) { + // Try to find and extract the field + if constexpr (concepts::optional_type) { + // For optional fields, it's ok if they're missing + auto field_result = find_field_unordered(key); + if (!field_result.error()) { + auto error = field_result.get(out.[:mem:]); + if (error && error != NO_SUCH_FIELD) { + return error; + } + } else if (field_result.error() != NO_SUCH_FIELD) { + return field_result.error(); + } else { + out.[:mem:].reset(); + } + } else { + // For required fields (in the requested list), fail if missing + SIMDJSON_TRY((*this)[key].get(out.[:mem:])); + } + } + } + }; + + return SUCCESS; +} + +#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION + } // namespace ondemand } // namespace ppc64 } // namespace simdjson @@ -84469,7 +95892,7 @@ simdjson_inline simdjson_result simdjson_result -simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { if (error()) { return error(); } return first.get_string(receiver, allow_replacement); } @@ -84505,12 +95928,12 @@ simdjson_deprecated simdjson_inline simdjson_result simdjson_result(first).get(); } template -simdjson_inline error_code simdjson_result::get(T &out) & noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) & noexcept { if (error()) { return error(); } return first.get(out); } template -simdjson_inline error_code simdjson_result::get(T &out) && noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) && noexcept { if (error()) { return error(); } return std::forward(first).get(out); } @@ -84520,8 +95943,8 @@ template<> simdjson_deprecated simdjson_inline simdjson_result(first); } -template<> simdjson_inline error_code simdjson_result::get(ppc64::ondemand::document &out) & noexcept = delete; -template<> simdjson_inline error_code simdjson_result::get(ppc64::ondemand::document &out) && noexcept { +template<> simdjson_warn_unused simdjson_inline error_code simdjson_result::get(ppc64::ondemand::document &out) & noexcept = delete; +template<> simdjson_warn_unused simdjson_inline error_code simdjson_result::get(ppc64::ondemand::document &out) && noexcept { if (error()) { return error(); } out = std::forward(first); return SUCCESS; @@ -84639,6 +96062,15 @@ simdjson_inline simdjson_result simdjson_result + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code simdjson_result::extract_into(T& out) & noexcept { + if (error()) { return error(); } + return first.extract_into(out); +} +#endif // SIMDJSON_STATIC_REFLECTION + } // namespace simdjson @@ -84673,7 +96105,7 @@ simdjson_inline simdjson_result document_reference::get_double() noexcep simdjson_inline simdjson_result document_reference::get_double_in_string() noexcept { return doc->get_root_value_iterator().get_root_double(false); } simdjson_inline simdjson_result document_reference::get_string(bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(false, allow_replacement); } template -simdjson_inline error_code document_reference::get_string(string_type& receiver, bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(receiver, false, allow_replacement); } +simdjson_warn_unused simdjson_inline error_code document_reference::get_string(string_type& receiver, bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(receiver, false, allow_replacement); } simdjson_inline simdjson_result document_reference::get_wobbly_string() noexcept { return doc->get_root_value_iterator().get_root_wobbly_string(false); } simdjson_inline simdjson_result document_reference::get_raw_json_string() noexcept { return doc->get_root_value_iterator().get_root_raw_json_string(false); } simdjson_inline simdjson_result document_reference::get_bool() noexcept { return doc->get_root_value_iterator().get_root_bool(false); } @@ -84726,7 +96158,13 @@ simdjson_inline simdjson_result document_reference::at_pointer(std::strin simdjson_inline simdjson_result document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); } simdjson_inline simdjson_result document_reference::raw_json() noexcept { return doc->raw_json();} simdjson_inline document_reference::operator document&() const noexcept { return *doc; } - +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code document_reference::extract_into(T& out) & noexcept { + return doc->extract_into(out); +} +#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION } // namespace ondemand } // namespace ppc64 } // namespace simdjson @@ -84823,7 +96261,7 @@ simdjson_inline simdjson_result simdjson_result -simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { if (error()) { return error(); } return first.get_string(receiver, allow_replacement); } @@ -84858,12 +96296,12 @@ simdjson_inline simdjson_result simdjson_result(first).get(); } template -simdjson_inline error_code simdjson_result::get(T &out) & noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) & noexcept { if (error()) { return error(); } return first.get(out); } template -simdjson_inline error_code simdjson_result::get(T &out) && noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) && noexcept { if (error()) { return error(); } return std::forward(first).get(out); } @@ -84880,13 +96318,13 @@ simdjson_inline simdjson_result simdjson_result -simdjson_inline error_code simdjson_result::get(ppc64::ondemand::document_reference &out) & noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(ppc64::ondemand::document_reference &out) & noexcept { if (error()) { return error(); } out = first; return SUCCESS; } template <> -simdjson_inline error_code simdjson_result::get(ppc64::ondemand::document_reference &out) && noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(ppc64::ondemand::document_reference &out) && noexcept { if (error()) { return error(); } out = first; return SUCCESS; @@ -84974,7 +96412,14 @@ simdjson_inline simdjson_result simdjson_result + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code simdjson_result::extract_into(T& out) & noexcept { + if (error()) { return error(); } + return first.extract_into(out); +} +#endif // SIMDJSON_STATIC_REFLECTION } // namespace simdjson #endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H @@ -85165,10 +96610,19 @@ simdjson_inline document_stream::iterator& document_stream::iterator::operator++ return *this; } +simdjson_inline bool document_stream::iterator::at_end() const noexcept { + return finished; +} + + simdjson_inline bool document_stream::iterator::operator!=(const document_stream::iterator &other) const noexcept { return finished != other.finished; } +simdjson_inline bool document_stream::iterator::operator==(const document_stream::iterator &other) const noexcept { + return finished == other.finished; +} + simdjson_inline document_stream::iterator document_stream::begin() noexcept { start(); // If there are no documents, we're finished. @@ -85286,7 +96740,10 @@ inline void document_stream::next_document() noexcept { // Always set depth=1 at the start of document doc.iter._depth = 1; // consume comma if comma separated is allowed - if (allow_comma_separated) { doc.iter.consume_character(','); } + if (allow_comma_separated) { + error_code ignored = doc.iter.consume_character(','); + static_cast(ignored); // ignored on purpose + } // Resets the string buffer at the beginning, thus invalidating the strings. doc.iter._string_buf_loc = parser->string_buf.get(); doc.iter._root = doc.iter.position(); @@ -85532,7 +96989,7 @@ simdjson_inline simdjson_result simdjson_result -simdjson_inline error_code simdjson_result::unescaped_key(string_type &receiver, bool allow_replacement) noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::unescaped_key(string_type &receiver, bool allow_replacement) noexcept { if (error()) { return error(); } return first.unescaped_key(receiver, allow_replacement); } @@ -85768,6 +97225,8 @@ simdjson_inline void json_iterator::assert_valid_position(token_position positio #ifndef SIMDJSON_CLANG_VISUAL_STUDIO SIMDJSON_ASSUME( position >= &parser->implementation->structural_indexes[0] ); SIMDJSON_ASSUME( position < &parser->implementation->structural_indexes[parser->implementation->n_structural_indexes] ); +#else + (void)position; // Suppress unused parameter warning #endif } @@ -85892,7 +97351,7 @@ simdjson_inline uint8_t *&json_iterator::string_buf_loc() noexcept { return _string_buf_loc; } -simdjson_inline error_code json_iterator::report_error(error_code _error, const char *message) noexcept { +simdjson_warn_unused simdjson_inline error_code json_iterator::report_error(error_code _error, const char *message) noexcept { SIMDJSON_ASSUME(_error != SUCCESS && _error != UNINITIALIZED && _error != INCORRECT_TYPE && _error != NO_SUCH_FIELD); logger::log_error(*this, message); error = _error; @@ -85936,7 +97395,7 @@ simdjson_inline void json_iterator::reenter_child(token_position position, depth _depth = child_depth; } -simdjson_inline error_code json_iterator::consume_character(char c) noexcept { +simdjson_warn_unused simdjson_inline error_code json_iterator::consume_character(char c) noexcept { if (*peek() == c) { return_current_and_advance(); return SUCCESS; @@ -85959,7 +97418,7 @@ simdjson_inline void json_iterator::set_start_position(depth_t depth, token_posi #endif -simdjson_inline error_code json_iterator::optional_error(error_code _error, const char *message) noexcept { +simdjson_warn_unused simdjson_inline error_code json_iterator::optional_error(error_code _error, const char *message) noexcept { SIMDJSON_ASSUME(_error == INCORRECT_TYPE || _error == NO_SUCH_FIELD); logger::log_error(*this, message); return _error; @@ -86354,6 +97813,10 @@ inline void log_line(const json_iterator &iter, token_position index, depth_t de /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value-inl.h" */ +/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_string_builder.h" // for constevalutil::fixed_string */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #endif */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { @@ -86411,7 +97874,7 @@ simdjson_inline simdjson_result object::start_root(value_iterator &iter) SIMDJSON_TRY( iter.start_root_object().error() ); return object(iter); } -simdjson_inline error_code object::consume() noexcept { +simdjson_warn_unused simdjson_inline error_code object::consume() noexcept { if(iter.is_at_key()) { /** * whenever you are pointing at a key, calling skip_child() is @@ -86540,6 +98003,52 @@ simdjson_inline simdjson_result object::reset() & noexcept { return iter.reset_object(); } +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION + +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code object::extract_into(T& out) & noexcept { + // Helper to check if a field name matches any of the requested fields + auto should_extract = [](std::string_view field_name) constexpr -> bool { + return ((FieldNames.view() == field_name) || ...); + }; + + // Iterate through all members of T using reflection + template for (constexpr auto mem : std::define_static_array( + std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) { + + if constexpr (!std::meta::is_const(mem) && std::meta::is_public(mem)) { + constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem)); + + // Only extract this field if it's in our list of requested fields + if constexpr (should_extract(key)) { + // Try to find and extract the field + if constexpr (concepts::optional_type) { + // For optional fields, it's ok if they're missing + auto field_result = find_field_unordered(key); + if (!field_result.error()) { + auto error = field_result.get(out.[:mem:]); + if (error && error != NO_SUCH_FIELD) { + return error; + } + } else if (field_result.error() != NO_SUCH_FIELD) { + return field_result.error(); + } else { + out.[:mem:].reset(); + } + } else { + // For required fields (in the requested list), fail if missing + SIMDJSON_TRY((*this)[key].get(out.[:mem:])); + } + } + } + }; + + return SUCCESS; +} + +#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION + } // namespace ondemand } // namespace ppc64 } // namespace simdjson @@ -86616,6 +98125,7 @@ simdjson_inline simdjson_result simdjson_result parser::iterate(padded_string_view json) & noexcept { - if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; } + if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; } json.remove_utf8_bom(); @@ -86830,7 +98340,7 @@ simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(p #ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON simdjson_warn_unused simdjson_inline simdjson_result parser::iterate_allow_incomplete_json(padded_string_view json) & noexcept { - if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; } + if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; } json.remove_utf8_bom(); @@ -86862,10 +98372,7 @@ simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(s } simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(std::string &json) & noexcept { - if(json.capacity() - json.size() < SIMDJSON_PADDING) { - json.reserve(json.size() + SIMDJSON_PADDING); - } - return iterate(padded_string_view(json)); + return iterate(pad_with_reserve(json)); } simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(const std::string &json) & noexcept { @@ -86887,7 +98394,7 @@ simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(c } simdjson_warn_unused simdjson_inline simdjson_result parser::iterate_raw(padded_string_view json) & noexcept { - if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; } + if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; } json.remove_utf8_bom(); @@ -86902,6 +98409,7 @@ simdjson_warn_unused simdjson_inline simdjson_result parser::iter } inline simdjson_result parser::iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept { + // Warning: no check is done on the buffer padding. We trust the user. if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; } if((len >= 3) && (std::memcmp(buf, "\xEF\xBB\xBF", 3) == 0)) { buf += 3; @@ -86910,16 +98418,24 @@ inline simdjson_result parser::iterate_many(const uint8_t *buf, if(allow_comma_separated && batch_size < len) { batch_size = len; } return document_stream(*this, buf, len, batch_size, allow_comma_separated); } + inline simdjson_result parser::iterate_many(const char *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept { + // Warning: no check is done on the buffer padding. We trust the user. return iterate_many(reinterpret_cast(buf), len, batch_size, allow_comma_separated); } -inline simdjson_result parser::iterate_many(const std::string &s, size_t batch_size, bool allow_comma_separated) noexcept { +inline simdjson_result parser::iterate_many(padded_string_view s, size_t batch_size, bool allow_comma_separated) noexcept { + if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; } return iterate_many(s.data(), s.length(), batch_size, allow_comma_separated); } inline simdjson_result parser::iterate_many(const padded_string &s, size_t batch_size, bool allow_comma_separated) noexcept { - return iterate_many(s.data(), s.length(), batch_size, allow_comma_separated); + return iterate_many(padded_string_view(s), batch_size, allow_comma_separated); +} +inline simdjson_result parser::iterate_many(const std::string &s, size_t batch_size, bool allow_comma_separated) noexcept { + return iterate_many(padded_string_view(s), batch_size, allow_comma_separated); +} +inline simdjson_result parser::iterate_many(std::string &s, size_t batch_size, bool allow_comma_separated) noexcept { + return iterate_many(pad(s), batch_size, allow_comma_separated); } - simdjson_pure simdjson_inline size_t parser::capacity() const noexcept { return _capacity; } @@ -86954,6 +98470,34 @@ simdjson_inline simdjson_warn_unused simdjson_result parser::u return result; } +simdjson_inline simdjson_warn_unused ondemand::parser& parser::get_parser() { + return *parser::get_parser_instance(); +} + +simdjson_inline bool release_parser() { + auto &parser_instance = parser::get_threadlocal_parser_if_exists(); + if (parser_instance) { + parser_instance.reset(); + return true; + } + return false; +} + +simdjson_inline simdjson_warn_unused std::unique_ptr& parser::get_parser_instance() { + std::unique_ptr& parser_instance = get_threadlocal_parser_if_exists(); + if (!parser_instance) { + parser_instance.reset(new ondemand::parser()); + } + return parser_instance; +} + +simdjson_inline simdjson_warn_unused std::unique_ptr& parser::get_threadlocal_parser_if_exists() { + // @the-moisrex points out that this could be implemented with std::optional (C++17). + thread_local std::unique_ptr parser_instance = nullptr; + return parser_instance; +} + + } // namespace ondemand } // namespace ppc64 } // namespace simdjson @@ -86988,8 +98532,13 @@ namespace ondemand { simdjson_inline raw_json_string::raw_json_string(const uint8_t * _buf) noexcept : buf{_buf} {} -simdjson_inline const char * raw_json_string::raw() const noexcept { return reinterpret_cast(buf); } +simdjson_inline const char * raw_json_string::raw() const noexcept { + return reinterpret_cast(buf); +} +simdjson_inline char raw_json_string::operator[](size_t i) const noexcept { + return reinterpret_cast(buf)[i]; +} simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(std::string_view target) noexcept { size_t pos{0}; @@ -87166,6 +98715,10 @@ simdjson_inline simdjson_result simdjson_result::operator[](size_t i) const noexcept { + if (error()) { return error(); } + return first[i]; +} simdjson_inline simdjson_warn_unused simdjson_result simdjson_result::unescape(ppc64::ondemand::json_iterator &iter, bool allow_replacement) const noexcept { if (error()) { return error(); } return first.unescape(iter, allow_replacement); @@ -87191,6 +98744,9 @@ simdjson_inline simdjson_warn_unused simdjson_result simdjson_ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/serialization.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */ +/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_builder.h" */ +/* amalgamation skipped (editor-only): #endif */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { @@ -87556,7 +99112,7 @@ simdjson_warn_unused simdjson_inline simdjson_result value_iterator::start if (*_json_iter->peek() == '}') { logger::log_value(*_json_iter, "empty object"); _json_iter->return_current_and_advance(); - end_container(); + SIMDJSON_TRY(end_container()); return false; } return true; @@ -88410,7 +99966,7 @@ simdjson_inline void value_iterator::advance_scalar(const char *type) noexcept { _json_iter->ascend_to(depth()-1); } -simdjson_inline error_code value_iterator::start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept { +simdjson_warn_unused simdjson_inline error_code value_iterator::start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept { logger::log_start_value(*_json_iter, start_position(), depth(), type); // If we're not at the position anymore, we don't want to advance the cursor. const uint8_t *json; @@ -88572,7 +100128,7 @@ simdjson_inline simdjson_result value_iterator::type() const noexcept case '5': case '6': case '7': case '8': case '9': return json_type::number; default: - return TAPE_ERROR; + return json_type::unknown; } } @@ -88612,6 +100168,1097 @@ simdjson_inline simdjson_result::simdjson_resul #endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H /* end file simdjson/generic/ondemand/value_iterator-inl.h for ppc64 */ +// JSON builder inline definitions +/* including simdjson/generic/ondemand/json_string_builder-inl.h for ppc64: #include "simdjson/generic/ondemand/json_string_builder-inl.h" */ +/* begin file simdjson/generic/ondemand/json_string_builder-inl.h for ppc64 */ +/** + * This file is part of the builder API. It is temporarily in the ondemand + * directory but we will move it to a builder directory later. + */ +#include +#include +#include +#ifndef SIMDJSON_GENERIC_STRING_BUILDER_INL_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_INL_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_string_builder.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +/* + * Empirically, we have found that an inlined optimization is important for + * performance. The following macros are not ideal. We should find a better + * way to inline the code. + */ + +#if defined(__SSE2__) || defined(__x86_64__) || defined(__x86_64) || \ + (defined(_M_AMD64) || defined(_M_X64) || \ + (defined(_M_IX86_FP) && _M_IX86_FP == 2)) +#ifndef SIMDJSON_EXPERIMENTAL_HAS_SSE2 +#define SIMDJSON_EXPERIMENTAL_HAS_SSE2 1 +#endif +#endif + +#if defined(__aarch64__) || defined(_M_ARM64) +#ifndef SIMDJSON_EXPERIMENTAL_HAS_NEON +#define SIMDJSON_EXPERIMENTAL_HAS_NEON 1 +#endif +#endif +#if SIMDJSON_EXPERIMENTAL_HAS_NEON +#include +#endif +#if SIMDJSON_EXPERIMENTAL_HAS_SSE2 +#include +#endif + +namespace simdjson { +namespace ppc64 { +namespace builder { + +static SIMDJSON_CONSTEXPR_LAMBDA std::array + json_quotable_character = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 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, 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, 1, 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, 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, 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, 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, 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, 0, 0, 0}; + +/** + +A possible SWAR implementation of has_json_escapable_byte. It is not used +because it is slower than the current implementation. It is kept here for +reference (to show that we tried it). + +inline bool has_json_escapable_byte(uint64_t x) { + uint64_t is_ascii = 0x8080808080808080ULL & ~x; + uint64_t xor2 = x ^ 0x0202020202020202ULL; + uint64_t lt32_or_eq34 = xor2 - 0x2121212121212121ULL; + uint64_t sub92 = x ^ 0x5C5C5C5C5C5C5C5CULL; + uint64_t eq92 = (sub92 - 0x0101010101010101ULL); + return ((lt32_or_eq34 | eq92) & is_ascii) != 0; +} + +**/ + +SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline bool +simple_needs_escaping(std::string_view v) { + for (char c : v) { + // a table lookup is faster than a series of comparisons + if (json_quotable_character[static_cast(c)]) { + return true; + } + } + return false; +} + +#if SIMDJSON_EXPERIMENTAL_HAS_NEON +simdjson_inline bool fast_needs_escaping(std::string_view view) { + if (view.size() < 16) { + return simple_needs_escaping(view); + } + size_t i = 0; + uint8x16_t running = vdupq_n_u8(0); + uint8x16_t v34 = vdupq_n_u8(34); + uint8x16_t v92 = vdupq_n_u8(92); + + for (; i + 15 < view.size(); i += 16) { + uint8x16_t word = vld1q_u8((const uint8_t *)view.data() + i); + running = vorrq_u8(running, vceqq_u8(word, v34)); + running = vorrq_u8(running, vceqq_u8(word, v92)); + running = vorrq_u8(running, vcltq_u8(word, vdupq_n_u8(32))); + } + if (i < view.size()) { + uint8x16_t word = + vld1q_u8((const uint8_t *)view.data() + view.length() - 16); + running = vorrq_u8(running, vceqq_u8(word, v34)); + running = vorrq_u8(running, vceqq_u8(word, v92)); + running = vorrq_u8(running, vcltq_u8(word, vdupq_n_u8(32))); + } + return vmaxvq_u32(vreinterpretq_u32_u8(running)) != 0; +} +#elif SIMDJSON_EXPERIMENTAL_HAS_SSE2 +simdjson_inline bool fast_needs_escaping(std::string_view view) { + if (view.size() < 16) { + return simple_needs_escaping(view); + } + size_t i = 0; + __m128i running = _mm_setzero_si128(); + for (; i + 15 < view.size(); i += 16) { + + __m128i word = + _mm_loadu_si128(reinterpret_cast(view.data() + i)); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(34))); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(92))); + running = _mm_or_si128( + running, _mm_cmpeq_epi8(_mm_subs_epu8(word, _mm_set1_epi8(31)), + _mm_setzero_si128())); + } + if (i < view.size()) { + __m128i word = _mm_loadu_si128( + reinterpret_cast(view.data() + view.length() - 16)); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(34))); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(92))); + running = _mm_or_si128( + running, _mm_cmpeq_epi8(_mm_subs_epu8(word, _mm_set1_epi8(31)), + _mm_setzero_si128())); + } + return _mm_movemask_epi8(running) != 0; +} +#else +simdjson_inline bool fast_needs_escaping(std::string_view view) { + return simple_needs_escaping(view); +} +#endif + +SIMDJSON_CONSTEXPR_LAMBDA inline size_t +find_next_json_quotable_character(const std::string_view view, + size_t location) noexcept { + + for (auto pos = view.begin() + location; pos != view.end(); ++pos) { + if (json_quotable_character[static_cast(*pos)]) { + return pos - view.begin(); + } + } + return size_t(view.size()); +} + +SIMDJSON_CONSTEXPR_LAMBDA static std::string_view control_chars[] = { + "\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", "\\u0006", + "\\u0007", "\\b", "\\t", "\\n", "\\u000b", "\\f", "\\r", + "\\u000e", "\\u000f", "\\u0010", "\\u0011", "\\u0012", "\\u0013", "\\u0014", + "\\u0015", "\\u0016", "\\u0017", "\\u0018", "\\u0019", "\\u001a", "\\u001b", + "\\u001c", "\\u001d", "\\u001e", "\\u001f"}; + +// All Unicode characters may be placed within the quotation marks, except for +// the characters that MUST be escaped: quotation mark, reverse solidus, and the +// control characters (U+0000 through U+001F). There are two-character sequence +// escape representations of some popular characters: +// \", \\, \b, \f, \n, \r, \t. +SIMDJSON_CONSTEXPR_LAMBDA void escape_json_char(char c, char *&out) { + if (c == '"') { + memcpy(out, "\\\"", 2); + out += 2; + } else if (c == '\\') { + memcpy(out, "\\\\", 2); + out += 2; + } else { + std::string_view v = control_chars[uint8_t(c)]; + memcpy(out, v.data(), v.size()); + out += v.size(); + } +} + +inline size_t write_string_escaped(const std::string_view input, char *out) { + size_t mysize = input.size(); + if (!fast_needs_escaping(input)) { // fast path! + memcpy(out, input.data(), input.size()); + return input.size(); + } + const char *const initout = out; + size_t location = find_next_json_quotable_character(input, 0); + memcpy(out, input.data(), location); + out += location; + escape_json_char(input[location], out); + location += 1; + while (location < mysize) { + size_t newlocation = find_next_json_quotable_character(input, location); + memcpy(out, input.data() + location, newlocation - location); + out += newlocation - location; + location = newlocation; + if (location == mysize) { + break; + } + escape_json_char(input[location], out); + location += 1; + } + return out - initout; +} + +simdjson_inline string_builder::string_builder(size_t initial_capacity) + : buffer(new(std::nothrow) char[initial_capacity]), position(0), + capacity(buffer.get() != nullptr ? initial_capacity : 0), + is_valid(buffer.get() != nullptr) {} + +simdjson_inline bool string_builder::capacity_check(size_t upcoming_bytes) { + // We use the convention that when is_valid is false, then the capacity and + // the position are 0. + // Most of the time, this function will return true. + if (simdjson_likely(upcoming_bytes <= capacity - position)) { + return true; + } + // check for overflow, most of the time there is no overflow + if (simdjson_likely(position + upcoming_bytes < position)) { + return false; + } + // We will rarely get here. + grow_buffer((std::max)(capacity * 2, position + upcoming_bytes)); + // If the buffer allocation failed, we set is_valid to false. + return is_valid; +} + +simdjson_inline void string_builder::grow_buffer(size_t desired_capacity) { + if (!is_valid) { + return; + } + std::unique_ptr new_buffer(new (std::nothrow) char[desired_capacity]); + if (new_buffer.get() == nullptr) { + set_valid(false); + return; + } + std::memcpy(new_buffer.get(), buffer.get(), position); + buffer.swap(new_buffer); + capacity = desired_capacity; +} + +simdjson_inline void string_builder::set_valid(bool valid) noexcept { + if (!valid) { + is_valid = false; + capacity = 0; + position = 0; + buffer.reset(); + } else { + is_valid = true; + } +} + +simdjson_inline size_t string_builder::size() const noexcept { + return position; +} + +simdjson_inline void string_builder::append(char c) noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = c; + } +} + +simdjson_inline void string_builder::append_null() noexcept { + constexpr char null_literal[] = "null"; + constexpr size_t null_len = sizeof(null_literal) - 1; + if (capacity_check(null_len)) { + std::memcpy(buffer.get() + position, null_literal, null_len); + position += null_len; + } +} + +simdjson_inline void string_builder::clear() noexcept { + position = 0; + // if it was invalid, we should try to repair it + if (!is_valid) { + capacity = 0; + buffer.reset(); + is_valid = true; + } +} + +namespace internal { + +template ::value>::type> +simdjson_really_inline int int_log2(number_type x) { + return 63 - leading_zeroes(uint64_t(x) | 1); +} + +simdjson_really_inline int fast_digit_count_32(uint32_t x) { + static uint64_t table[] = { + 4294967296, 8589934582, 8589934582, 8589934582, 12884901788, + 12884901788, 12884901788, 17179868184, 17179868184, 17179868184, + 21474826480, 21474826480, 21474826480, 21474826480, 25769703776, + 25769703776, 25769703776, 30063771072, 30063771072, 30063771072, + 34349738368, 34349738368, 34349738368, 34349738368, 38554705664, + 38554705664, 38554705664, 41949672960, 41949672960, 41949672960, + 42949672960, 42949672960}; + return uint32_t((x + table[int_log2(x)]) >> 32); +} + +simdjson_really_inline int fast_digit_count_64(uint64_t x) { + static uint64_t table[] = {9, + 99, + 999, + 9999, + 99999, + 999999, + 9999999, + 99999999, + 999999999, + 9999999999, + 99999999999, + 999999999999, + 9999999999999, + 99999999999999, + 999999999999999ULL, + 9999999999999999ULL, + 99999999999999999ULL, + 999999999999999999ULL, + 9999999999999999999ULL}; + int y = (19 * int_log2(x) >> 6); + y += x > table[y]; + return y + 1; +} + +template ::value>::type> +simdjson_really_inline size_t digit_count(number_type v) noexcept { + static_assert(sizeof(number_type) == 8 || sizeof(number_type) == 4 || + sizeof(number_type) == 2 || sizeof(number_type) == 1, + "We only support 8-bit, 16-bit, 32-bit and 64-bit numbers"); + SIMDJSON_IF_CONSTEXPR(sizeof(number_type) <= 4) { + return fast_digit_count_32(static_cast(v)); + } + else { + return fast_digit_count_64(static_cast(v)); + } +} +static const char decimal_table[200] = { + 0x30, 0x30, 0x30, 0x31, 0x30, 0x32, 0x30, 0x33, 0x30, 0x34, 0x30, 0x35, + 0x30, 0x36, 0x30, 0x37, 0x30, 0x38, 0x30, 0x39, 0x31, 0x30, 0x31, 0x31, + 0x31, 0x32, 0x31, 0x33, 0x31, 0x34, 0x31, 0x35, 0x31, 0x36, 0x31, 0x37, + 0x31, 0x38, 0x31, 0x39, 0x32, 0x30, 0x32, 0x31, 0x32, 0x32, 0x32, 0x33, + 0x32, 0x34, 0x32, 0x35, 0x32, 0x36, 0x32, 0x37, 0x32, 0x38, 0x32, 0x39, + 0x33, 0x30, 0x33, 0x31, 0x33, 0x32, 0x33, 0x33, 0x33, 0x34, 0x33, 0x35, + 0x33, 0x36, 0x33, 0x37, 0x33, 0x38, 0x33, 0x39, 0x34, 0x30, 0x34, 0x31, + 0x34, 0x32, 0x34, 0x33, 0x34, 0x34, 0x34, 0x35, 0x34, 0x36, 0x34, 0x37, + 0x34, 0x38, 0x34, 0x39, 0x35, 0x30, 0x35, 0x31, 0x35, 0x32, 0x35, 0x33, + 0x35, 0x34, 0x35, 0x35, 0x35, 0x36, 0x35, 0x37, 0x35, 0x38, 0x35, 0x39, + 0x36, 0x30, 0x36, 0x31, 0x36, 0x32, 0x36, 0x33, 0x36, 0x34, 0x36, 0x35, + 0x36, 0x36, 0x36, 0x37, 0x36, 0x38, 0x36, 0x39, 0x37, 0x30, 0x37, 0x31, + 0x37, 0x32, 0x37, 0x33, 0x37, 0x34, 0x37, 0x35, 0x37, 0x36, 0x37, 0x37, + 0x37, 0x38, 0x37, 0x39, 0x38, 0x30, 0x38, 0x31, 0x38, 0x32, 0x38, 0x33, + 0x38, 0x34, 0x38, 0x35, 0x38, 0x36, 0x38, 0x37, 0x38, 0x38, 0x38, 0x39, + 0x39, 0x30, 0x39, 0x31, 0x39, 0x32, 0x39, 0x33, 0x39, 0x34, 0x39, 0x35, + 0x39, 0x36, 0x39, 0x37, 0x39, 0x38, 0x39, 0x39, +}; +} // namespace internal + +template +simdjson_inline void string_builder::append(number_type v) noexcept { + static_assert(std::is_same::value || + std::is_integral::value || + std::is_floating_point::value, + "Unsupported number type"); + // If C++17 is available, we can 'if constexpr' here. + SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + if (v) { + constexpr char true_literal[] = "true"; + constexpr size_t true_len = sizeof(true_literal) - 1; + if (capacity_check(true_len)) { + std::memcpy(buffer.get() + position, true_literal, true_len); + position += true_len; + } + } else { + constexpr char false_literal[] = "false"; + constexpr size_t false_len = sizeof(false_literal) - 1; + if (capacity_check(false_len)) { + std::memcpy(buffer.get() + position, false_literal, false_len); + position += false_len; + } + } + } + else SIMDJSON_IF_CONSTEXPR(std::is_unsigned::value) { + constexpr size_t max_number_size = 20; + if (capacity_check(max_number_size)) { + using unsigned_type = typename std::make_unsigned::type; + unsigned_type pv = static_cast(v); + size_t dc = internal::digit_count(pv); + char *write_pointer = buffer.get() + position + dc - 1; + while (pv >= 100) { + memcpy(write_pointer - 1, &internal::decimal_table[(pv % 100) * 2], 2); + write_pointer -= 2; + pv /= 100; + } + if (pv >= 10) { + *write_pointer-- = char('0' + (pv % 10)); + pv /= 10; + } + *write_pointer = char('0' + pv); + position += dc; + } + } + else SIMDJSON_IF_CONSTEXPR(std::is_integral::value) { + constexpr size_t max_number_size = 20; + if (capacity_check(max_number_size)) { + using unsigned_type = typename std::make_unsigned::type; + bool negative = v < 0; + unsigned_type pv = static_cast(v); + if (negative) { + pv = 0 - pv; // the 0 is for Microsoft + } + size_t dc = internal::digit_count(pv); + if (negative) { + buffer.get()[position++] = '-'; + } + char *write_pointer = buffer.get() + position + dc - 1; + while (pv >= 100) { + memcpy(write_pointer - 1, &internal::decimal_table[(pv % 100) * 2], 2); + write_pointer -= 2; + pv /= 100; + } + if (pv >= 10) { + *write_pointer-- = char('0' + (pv % 10)); + pv /= 10; + } + *write_pointer = char('0' + pv); + position += dc; + } + } + else SIMDJSON_IF_CONSTEXPR(std::is_floating_point::value) { + constexpr size_t max_number_size = 24; + if (capacity_check(max_number_size)) { + // We could specialize for float. + char *end = simdjson::internal::to_chars(buffer.get() + position, nullptr, + double(v)); + position = end - buffer.get(); + } + } +} + +simdjson_inline void +string_builder::escape_and_append(std::string_view input) noexcept { + // escaping might turn a control character into \x00xx so 6 characters. + if (capacity_check(6 * input.size())) { + position += write_string_escaped(input, buffer.get() + position); + } +} + +simdjson_inline void +string_builder::escape_and_append_with_quotes(std::string_view input) noexcept { + // escaping might turn a control character into \x00xx so 6 characters. + if (capacity_check(2 + 6 * input.size())) { + buffer.get()[position++] = '"'; + position += write_string_escaped(input, buffer.get() + position); + buffer.get()[position++] = '"'; + } +} + +simdjson_inline void +string_builder::escape_and_append_with_quotes(char input) noexcept { + // escaping might turn a control character into \x00xx so 6 characters. + if (capacity_check(2 + 6 * 1)) { + buffer.get()[position++] = '"'; + std::string_view cinput(&input, 1); + position += write_string_escaped(cinput, buffer.get() + position); + buffer.get()[position++] = '"'; + } +} + +simdjson_inline void +string_builder::escape_and_append_with_quotes(const char *input) noexcept { + std::string_view cinput(input); + escape_and_append_with_quotes(cinput); +} +#if SIMDJSON_SUPPORTS_CONCEPTS +template +simdjson_inline void string_builder::escape_and_append_with_quotes() noexcept { + escape_and_append_with_quotes(constevalutil::string_constant::value); +} +#endif + +simdjson_inline void string_builder::append_raw(const char *c) noexcept { + size_t len = std::strlen(c); + append_raw(c, len); +} + +simdjson_inline void +string_builder::append_raw(std::string_view input) noexcept { + if (capacity_check(input.size())) { + std::memcpy(buffer.get() + position, input.data(), input.size()); + position += input.size(); + } +} + +simdjson_inline void string_builder::append_raw(const char *str, + size_t len) noexcept { + if (capacity_check(len)) { + std::memcpy(buffer.get() + position, str, len); + position += len; + } +} +#if SIMDJSON_SUPPORTS_CONCEPTS +// Support for optional types (std::optional, etc.) +template + requires(!require_custom_serialization) +simdjson_inline void string_builder::append(const T &opt) { + if (opt) { + append(*opt); + } else { + append_null(); + } +} + +template + requires(require_custom_serialization) +simdjson_inline void string_builder::append(const T &val) { + serialize(*this, val); +} + +template + requires(std::is_convertible::value || + std::is_same::value) +simdjson_inline void string_builder::append(const T &value) { + escape_and_append_with_quotes(value); +} +#endif + +#if SIMDJSON_SUPPORTS_RANGES && SIMDJSON_SUPPORTS_CONCEPTS +// Support for range-based appending (std::ranges::view, etc.) +template + requires(!std::is_convertible::value) +simdjson_inline void string_builder::append(const R &range) noexcept { + auto it = std::ranges::begin(range); + auto end = std::ranges::end(range); + if constexpr (concepts::is_pair) { + start_object(); + + if (it == end) { + end_object(); + return; // Handle empty range + } + // Append first item without leading comma + append_key_value(it->first, it->second); + ++it; + + // Append remaining items with preceding commas + for (; it != end; ++it) { + append_comma(); + append_key_value(it->first, it->second); + } + end_object(); + } else { + start_array(); + if (it == end) { + end_array(); + return; // Handle empty range + } + + // Append first item without leading comma + append(*it); + ++it; + + // Append remaining items with preceding commas + for (; it != end; ++it) { + append_comma(); + append(*it); + } + end_array(); + } +} + +#endif + +#if SIMDJSON_EXCEPTIONS +simdjson_inline string_builder::operator std::string() const noexcept(false) { + return std::string(operator std::string_view()); +} + +simdjson_inline string_builder::operator std::string_view() const + noexcept(false) simdjson_lifetime_bound { + return view(); +} +#endif + +simdjson_inline simdjson_result +string_builder::view() const noexcept { + if (!is_valid) { + return simdjson::OUT_OF_CAPACITY; + } + return std::string_view(buffer.get(), position); +} + +simdjson_inline simdjson_result string_builder::c_str() noexcept { + if (capacity_check(1)) { + buffer.get()[position] = '\0'; + return buffer.get(); + } + return simdjson::OUT_OF_CAPACITY; +} + +simdjson_inline bool string_builder::validate_unicode() const noexcept { + return simdjson::validate_utf8(buffer.get(), position); +} + +simdjson_inline void string_builder::start_object() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = '{'; + } +} + +simdjson_inline void string_builder::end_object() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = '}'; + } +} + +simdjson_inline void string_builder::start_array() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = '['; + } +} + +simdjson_inline void string_builder::end_array() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = ']'; + } +} + +simdjson_inline void string_builder::append_comma() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = ','; + } +} + +simdjson_inline void string_builder::append_colon() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = ':'; + } +} + +template +simdjson_inline void +string_builder::append_key_value(key_type key, value_type value) noexcept { + static_assert(std::is_same::value || + std::is_convertible::value, + "Unsupported key type"); + escape_and_append_with_quotes(key); + append_colon(); + SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + append_null(); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR( + std::is_convertible::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else { + append(value); + } +} + +#if SIMDJSON_SUPPORTS_CONCEPTS +template +simdjson_inline void +string_builder::append_key_value(value_type value) noexcept { + escape_and_append_with_quotes(); + append_colon(); + SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + append_null(); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR( + std::is_convertible::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else { + append(value); + } +} +#endif + +} // namespace builder +} // namespace ppc64 +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_STRING_BUILDER_INL_H +/* end file simdjson/generic/ondemand/json_string_builder-inl.h for ppc64 */ +/* including simdjson/generic/ondemand/json_builder.h for ppc64: #include "simdjson/generic/ondemand/json_builder.h" */ +/* begin file simdjson/generic/ondemand/json_builder.h for ppc64 */ +/** + * This file is part of the builder API. It is temporarily in the ondemand directory + * but we will move it to a builder directory later. + */ +#ifndef SIMDJSON_GENERIC_BUILDER_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_string_builder.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/concepts.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ +#if SIMDJSON_STATIC_REFLECTION + +#include +#include +#include +#include +#include +#include +#include +#include +// #include // for std::define_static_string - header not available yet + +namespace simdjson { +namespace ppc64 { +namespace builder { + +template + requires(concepts::container_but_not_string && !require_custom_serialization) +constexpr void atom(string_builder &b, const T &t) { + auto it = t.begin(); + auto end = t.end(); + if (it == end) { + b.append_raw("[]"); + return; + } + b.append('['); + atom(b, *it); + ++it; + for (; it != end; ++it) { + b.append(','); + atom(b, *it); + } + b.append(']'); +} + +template + requires(std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v) +constexpr void atom(string_builder &b, const T &t) { + b.escape_and_append_with_quotes(t); +} + +template + requires(!require_custom_serialization) +constexpr void atom(string_builder &b, const T &m) { + if (m.empty()) { + b.append_raw("{}"); + return; + } + b.append('{'); + bool first = true; + for (const auto& [key, value] : m) { + if (!first) { + b.append(','); + } + first = false; + // Keys must be convertible to string_view per the concept + b.escape_and_append_with_quotes(key); + b.append(':'); + atom(b, value); + } + b.append('}'); +} + + +template::value && !std::is_same_v>::type> +constexpr void atom(string_builder &b, const number_type t) { + b.append(t); +} + +template + requires(std::is_class_v && !concepts::container_but_not_string && + !concepts::string_view_keyed_map && + !concepts::optional_type && + !concepts::smart_pointer && + !concepts::appendable_containers && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && !require_custom_serialization) +constexpr void atom(string_builder &b, const T &t) { + int i = 0; + b.append('{'); + template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) { + if (i != 0) + b.append(','); + constexpr auto key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(dm))); + b.append_raw(key); + b.append(':'); + atom(b, t.[:dm:]); + i++; + }; + b.append('}'); +} + +// Support for optional types (std::optional, etc.) +template + requires(!require_custom_serialization) +constexpr void atom(string_builder &b, const T &opt) { + if (opt) { + atom(b, opt.value()); + } else { + b.append_raw("null"); + } +} + +// Support for smart pointers (std::unique_ptr, std::shared_ptr, etc.) +template + requires(!require_custom_serialization) +constexpr void atom(string_builder &b, const T &ptr) { + if (ptr) { + atom(b, *ptr); + } else { + b.append_raw("null"); + } +} + +// Support for enums - serialize as string representation using expand approach from P2996R12 +template + requires(std::is_enum_v && !require_custom_serialization) +void atom(string_builder &b, const T &e) { +#if SIMDJSON_STATIC_REFLECTION + constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T)); + template for (constexpr auto enum_val : enumerators) { + constexpr auto enum_str = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(enum_val))); + if (e == [:enum_val:]) { + b.append_raw(enum_str); + return; + } + }; + // Fallback to integer if enum value not found + atom(b, static_cast>(e)); +#else + // Fallback: serialize as integer if reflection not available + atom(b, static_cast>(e)); +#endif +} + +// Support for appendable containers that don't have operator[] (sets, etc.) +template + requires(!concepts::container_but_not_string && !concepts::string_view_keyed_map && + !concepts::optional_type && !concepts::smart_pointer && + !std::is_same_v && + !std::is_same_v && !std::is_same_v && !require_custom_serialization) +constexpr void atom(string_builder &b, const T &container) { + if (container.empty()) { + b.append_raw("[]"); + return; + } + b.append('['); + bool first = true; + for (const auto& item : container) { + if (!first) { + b.append(','); + } + first = false; + atom(b, item); + } + b.append(']'); +} + +// append functions that delegate to atom functions for primitive types +template + requires(std::is_arithmetic_v && !std::is_same_v) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!concepts::container_but_not_string && !concepts::string_view_keyed_map && + !concepts::optional_type && !concepts::smart_pointer && + !std::is_same_v && + !std::is_same_v && !std::is_same_v && !require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +// works for struct +template + requires(std::is_class_v && !concepts::container_but_not_string && + !concepts::string_view_keyed_map && + !concepts::optional_type && + !concepts::smart_pointer && + !concepts::appendable_containers && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && !require_custom_serialization) +void append(string_builder &b, const Z &z) { + int i = 0; + b.append('{'); + template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^Z, std::meta::access_context::unchecked()))) { + if (i != 0) + b.append(','); + constexpr auto key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(dm))); + b.append_raw(key); + b.append(':'); + atom(b, z.[:dm:]); + i++; + }; + b.append('}'); +} + +// works for container that have begin() and end() iterators +template + requires(concepts::container_but_not_string && !require_custom_serialization) +void append(string_builder &b, const Z &z) { + auto it = z.begin(); + auto end = z.end(); + if (it == end) { + b.append_raw("[]"); + return; + } + b.append('['); + atom(b, *it); + ++it; + for (; it != end; ++it) { + b.append(','); + atom(b, *it); + } + b.append(']'); +} + +template + requires (require_custom_serialization) +void append(string_builder &b, const Z &z) { + b.append(z); +} + + +template +simdjson_warn_unused simdjson_result to_json_string(const Z &z, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) { + string_builder b(initial_capacity); + append(b, z); + std::string_view s; + if(auto e = b.view().get(s); e) { return e; } + return std::string(s); +} + +template +simdjson_warn_unused simdjson_error to_json(const Z &z, std::string &s, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) { + string_builder b(initial_capacity); + append(b, z); + std::string_view view; + if(auto e = b.view().get(view); e) { return e; } + s.assign(view); + return SUCCESS; +} + +template +string_builder& operator<<(string_builder& b, const Z& z) { + append(b, z); + return b; +} + +// extract_from: Serialize only specific fields from a struct to JSON +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +void extract_from(string_builder &b, const T &obj) { + // Helper to check if a field name matches any of the requested fields + auto should_extract = [](std::string_view field_name) constexpr -> bool { + return ((FieldNames.view() == field_name) || ...); + }; + + b.append('{'); + bool first = true; + + // Iterate through all members of T using reflection + template for (constexpr auto mem : std::define_static_array( + std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) { + + if constexpr (std::meta::is_public(mem)) { + constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem)); + + // Only serialize this field if it's in our list of requested fields + if constexpr (should_extract(key)) { + if (!first) { + b.append(','); + } + first = false; + + // Serialize the key + constexpr auto quoted_key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(mem))); + b.append_raw(quoted_key); + b.append(':'); + + // Serialize the value + atom(b, obj.[:mem:]); + } + } + }; + + b.append('}'); +} + +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_result extract_from(const T &obj, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) { + string_builder b(initial_capacity); + extract_from(b, obj); + std::string_view s; + if(auto e = b.view().get(s); e) { return e; } + return std::string(s); +} + +} // namespace builder +} // namespace ppc64 +// Alias the function template to 'to' in the global namespace +template +simdjson_warn_unused simdjson_result to_json(const Z &z, size_t initial_capacity = ppc64::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { + ppc64::builder::string_builder b(initial_capacity); + ppc64::builder::append(b, z); + std::string_view s; + if(auto e = b.view().get(s); e) { return e; } + return std::string(s); +} +template +simdjson_warn_unused simdjson_error to_json(const Z &z, std::string &s, size_t initial_capacity = ppc64::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { + ppc64::builder::string_builder b(initial_capacity); + ppc64::builder::append(b, z); + std::string_view view; + if(auto e = b.view().get(view); e) { return e; } + s.assign(view); + return SUCCESS; +} +// Global namespace function for extract_from +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_result extract_from(const T &obj, size_t initial_capacity = ppc64::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { + ppc64::builder::string_builder b(initial_capacity); + ppc64::builder::extract_from(b, obj); + std::string_view s; + if(auto e = b.view().get(s); e) { return e; } + return std::string(s); +} + +} // namespace simdjson + +#endif // SIMDJSON_STATIC_REFLECTION + +#endif +/* end file simdjson/generic/ondemand/json_builder.h for ppc64 */ /* end file simdjson/generic/ondemand/amalgamated.h for ppc64 */ /* including simdjson/ppc64/end.h: #include "simdjson/ppc64/end.h" */ @@ -89729,7 +102376,7 @@ using namespace simd; struct backslash_and_quote { public: static constexpr uint32_t BYTES_PROCESSED = 32; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); + simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; } simdjson_inline bool has_backslash() { return bs_bits != 0; } @@ -89755,6 +102402,31 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin }; } + +struct escaping { + static constexpr uint32_t BYTES_PROCESSED = 16; + simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst); + + simdjson_inline bool has_escape() { return escape_bits != 0; } + simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); } + + uint64_t escape_bits; +}; // struct escaping + + + +simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) { + static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes"); + simd8 v(src); + v.store(dst); + simd8 is_quote = (v == '"'); + simd8 is_backslash = (v == '\\'); + simd8 is_control = (v < 32); + return { + uint64_t((is_backslash | is_quote | is_control).to_bitmask()) + }; +} + } // unnamed namespace } // namespace westmere } // namespace simdjson @@ -89821,7 +102493,7 @@ class value_iterator; /* end file simdjson/generic/ondemand/base.h for westmere */ /* including simdjson/generic/ondemand/deserialize.h for westmere: #include "simdjson/generic/ondemand/deserialize.h" */ /* begin file simdjson/generic/ondemand/deserialize.h for westmere */ -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS #ifndef SIMDJSON_ONDEMAND_DESERIALIZE_H /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ @@ -89830,55 +102502,8 @@ class value_iterator; /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ -#include namespace simdjson { -namespace tag_invoke_fn_ns { -void tag_invoke(); - -struct tag_invoke_fn { - template - requires requires(Tag tag, Args &&...args) { - tag_invoke(std::forward(tag), std::forward(args)...); - } - constexpr auto operator()(Tag tag, Args &&...args) const - noexcept(noexcept(tag_invoke(std::forward(tag), - std::forward(args)...))) - -> decltype(tag_invoke(std::forward(tag), - std::forward(args)...)) { - return tag_invoke(std::forward(tag), std::forward(args)...); - } -}; -} // namespace tag_invoke_fn_ns - -inline namespace tag_invoke_ns { -inline constexpr tag_invoke_fn_ns::tag_invoke_fn tag_invoke = {}; -} // namespace tag_invoke_ns - -template -concept tag_invocable = requires(Tag tag, Args... args) { - tag_invoke(std::forward(tag), std::forward(args)...); -}; - -template -concept nothrow_tag_invocable = - tag_invocable && requires(Tag tag, Args... args) { - { - tag_invoke(std::forward(tag), std::forward(args)...) - } noexcept; - }; - -template -using tag_invoke_result = - std::invoke_result; - -template -using tag_invoke_result_t = - std::invoke_result_t; - -template using tag_t = std::decay_t; - - struct deserialize_tag; /// These types are deserializable in a built-in way @@ -89900,7 +102525,7 @@ template concept custom_deserializable = tag_invocable; template -concept deserializable = custom_deserializable || is_builtin_deserializable_v; +concept deserializable = custom_deserializable || is_builtin_deserializable_v || concepts::optional_type; template concept nothrow_custom_deserializable = nothrow_tag_invocable; @@ -89911,28 +102536,44 @@ concept nothrow_deserializable = nothrow_custom_deserializable || is_bu /// Deserialize Tag inline constexpr struct deserialize_tag { + using array_type = westmere::ondemand::array; + using object_type = westmere::ondemand::object; using value_type = westmere::ondemand::value; using document_type = westmere::ondemand::document; using document_reference_type = westmere::ondemand::document_reference; + // Customization Point for array + template + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(array_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for object + template + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(object_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + // Customization Point for value template requires custom_deserializable - [[nodiscard]] constexpr /* error_code */ auto operator()(value_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + simdjson_warn_unused constexpr /* error_code */ auto operator()(value_type &object, T& output) const noexcept(nothrow_custom_deserializable) { return tag_invoke(*this, object, output); } // Customization Point for document template requires custom_deserializable - [[nodiscard]] constexpr /* error_code */ auto operator()(document_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + simdjson_warn_unused constexpr /* error_code */ auto operator()(document_type &object, T& output) const noexcept(nothrow_custom_deserializable) { return tag_invoke(*this, object, output); } // Customization Point for document reference template requires custom_deserializable - [[nodiscard]] constexpr /* error_code */ auto operator()(document_reference_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + simdjson_warn_unused constexpr /* error_code */ auto operator()(document_reference_type &object, T& output) const noexcept(nothrow_custom_deserializable) { return tag_invoke(*this, object, output); } @@ -89942,7 +102583,7 @@ inline constexpr struct deserialize_tag { } // namespace simdjson #endif // SIMDJSON_ONDEMAND_DESERIALIZE_H -#endif // SIMDJSON_SUPPORTS_DESERIALIZATION +#endif // SIMDJSON_SUPPORTS_CONCEPTS /* end file simdjson/generic/ondemand/deserialize.h for westmere */ /* including simdjson/generic/ondemand/value_iterator.h for westmere: #include "simdjson/generic/ondemand/value_iterator.h" */ @@ -90284,7 +102925,7 @@ class value_iterator { simdjson_warn_unused simdjson_inline simdjson_result get_root_number(bool check_trailing) noexcept; simdjson_warn_unused simdjson_inline simdjson_result is_root_null(bool check_trailing) noexcept; - simdjson_inline error_code error() const noexcept; + simdjson_warn_unused simdjson_inline error_code error() const noexcept; simdjson_inline uint8_t *&string_buf_loc() noexcept; simdjson_inline const json_iterator &json_iter() const noexcept; simdjson_inline json_iterator &json_iter() noexcept; @@ -90368,8 +103009,8 @@ class value_iterator { simdjson_inline const uint8_t *peek_non_root_scalar(const char *type) noexcept; - simdjson_inline error_code start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept; - simdjson_inline error_code end_container() noexcept; + simdjson_warn_unused simdjson_inline error_code start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept; + simdjson_warn_unused simdjson_inline error_code end_container() noexcept; /** * Advance to a place expecting a value (increasing depth). @@ -90379,8 +103020,8 @@ class value_iterator { */ simdjson_inline simdjson_result advance_to_value() noexcept; - simdjson_inline error_code incorrect_type_error(const char *message) const noexcept; - simdjson_inline error_code error_unless_more_tokens(uint32_t tokens=1) const noexcept; + simdjson_warn_unused simdjson_inline error_code incorrect_type_error(const char *message) const noexcept; + simdjson_warn_unused simdjson_inline error_code error_unless_more_tokens(uint32_t tokens=1) const noexcept; simdjson_inline bool is_at_start() const noexcept; /** @@ -90417,7 +103058,7 @@ class value_iterator { /** @copydoc error_code json_iterator::end_position() const noexcept; */ simdjson_inline token_position end_position() const noexcept; /** @copydoc error_code json_iterator::report_error(error_code error, const char *message) noexcept; */ - simdjson_inline error_code report_error(error_code error, const char *message) noexcept; + simdjson_warn_unused simdjson_inline error_code report_error(error_code error, const char *message) noexcept; friend class document; friend class object; @@ -90482,13 +103123,14 @@ class value { * * You may use get_double(), get_bool(), get_uint64(), get_int64(), * get_object(), get_array(), get_raw_json_string(), or get_string() instead. + * When SIMDJSON_SUPPORTS_CONCEPTS is set, custom types are also supported. * * @returns A value of the given type, parsed from the JSON. * @returns INCORRECT_TYPE If the JSON value is not the given type. */ template simdjson_inline simdjson_result get() -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept @@ -90505,22 +103147,38 @@ class value { * Get this value as the given type. * * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool + * If the macro SIMDJSON_SUPPORTS_CONCEPTS is set, then custom types are also supported. * * @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized. * @returns INCORRECT_TYPE If the JSON value is not an object. * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. */ template - simdjson_inline error_code get(T &out) -#if SIMDJSON_SUPPORTS_DESERIALIZATION + simdjson_warn_unused simdjson_inline error_code get(T &out) +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept #endif { - #if SIMDJSON_SUPPORTS_DESERIALIZATION + #if SIMDJSON_SUPPORTS_CONCEPTS if constexpr (custom_deserializable) { return deserialize(*this, out); + } else if constexpr (concepts::optional_type) { + using value_type = typename std::remove_cvref_t::value_type; + + // Check if the value is null + bool is_null_value; + SIMDJSON_TRY( is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); // Set to nullopt + return SUCCESS; + } + + if (!out) { + out.emplace(); + } + return get(out.value()); } else { static_assert(!sizeof(T), "The get method with type T is not implemented by the simdjson library. " "And you do not seem to have added support for it. Indeed, we have that " @@ -90530,7 +103188,7 @@ class value { static_cast(out); // to get rid of unused errors return UNINITIALIZED; } -#else // SIMDJSON_SUPPORTS_DESERIALIZATION +#else // SIMDJSON_SUPPORTS_CONCEPTS // Unless the simdjson library or the user provides an inline implementation, calling this method should // immediately fail. static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " @@ -90648,7 +103306,7 @@ class value { * @returns INCORRECT_TYPE if the JSON value is not a string. Otherwise, we return SUCCESS. */ template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; /** * Cast this JSON value to a "wobbly" string. @@ -91169,7 +103827,7 @@ struct simdjson_result : public westmere::implementat simdjson_inline simdjson_result get_double_in_string() noexcept; simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; simdjson_inline simdjson_result get_wobbly_string() noexcept; simdjson_inline simdjson_result get_raw_json_string() noexcept; simdjson_inline simdjson_result get_bool() noexcept; @@ -91250,7 +103908,22 @@ struct simdjson_result : public westmere::implementat simdjson_result operator[](int) noexcept = delete; /** - * Get the type of this JSON value. + * Get the type of this JSON value. It does not validate or consume the value. + * E.g., you must still call "is_null()" to check that a value is null even if + * "type()" returns json_type::null. + * + * Given a valid JSON document, the answer can be one of + * simdjson::ondemand::json_type::object, + * simdjson::ondemand::json_type::array, + * simdjson::ondemand::json_type::string, + * simdjson::ondemand::json_type::number, + * simdjson::ondemand::json_type::boolean, + * simdjson::ondemand::json_type::null. + * + * Starting with simdjson 4.0, this function will return simdjson::ondemand::json_type::unknown + * given a bad token. + * This allows you to identify a case such as {"key": NaN} and identify the NaN value. + * The simdjson::ondemand::json_type::unknown value should only happen with non-valid JSON. * * NOTE: If you're only expecting a value to be one type (a typical case), it's generally * better to just call .get_double, .get_string, etc. and check for INCORRECT_TYPE (or just @@ -91744,14 +104417,14 @@ class json_iterator { * @param error The error to report. Must not be SUCCESS, UNINITIALIZED, INCORRECT_TYPE, or NO_SUCH_FIELD. * @param message An error message to report with the error. */ - simdjson_inline error_code report_error(error_code error, const char *message) noexcept; + simdjson_warn_unused simdjson_inline error_code report_error(error_code error, const char *message) noexcept; /** * Log error, but don't stop iteration. * @param error The error to report. Must be INCORRECT_TYPE, or NO_SUCH_FIELD. * @param message An error message to report with the error. */ - simdjson_inline error_code optional_error(error_code error, const char *message) noexcept; + simdjson_warn_unused simdjson_inline error_code optional_error(error_code error, const char *message) noexcept; /** * Take an input in json containing max_len characters and attempt to copy it over to tmpbuf, a buffer with @@ -91771,7 +104444,7 @@ class json_iterator { simdjson_inline void reenter_child(token_position position, depth_t child_depth) noexcept; - simdjson_inline error_code consume_character(char c) noexcept; + simdjson_warn_unused simdjson_inline error_code consume_character(char c) noexcept; #if SIMDJSON_DEVELOPMENT_CHECKS simdjson_inline token_position start_position(depth_t depth) const noexcept; simdjson_inline void set_start_position(depth_t depth, token_position position) noexcept; @@ -91862,6 +104535,7 @@ namespace ondemand { * The type of a JSON value. */ enum class json_type { + unknown=0, // Start at 1 to catch uninitialized / default values more easily array=1, ///< A JSON array ( [ 1, 2, 3 ... ] ) object, ///< A JSON object ( { "a": 1, "b" 2, ... } ) @@ -92068,6 +104742,12 @@ class raw_json_string { */ simdjson_inline const char * raw() const noexcept; + /** + * Get the character at index i. This is unchecked. + * [0] when the string is of length 0 returns the final quote ("). + */ + simdjson_inline char operator[](size_t i) const noexcept; + /** * This compares the current instance to the std::string_view target: returns true if * they are byte-by-byte equal (no escaping is done) on target.size() characters, @@ -92207,10 +104887,10 @@ struct simdjson_result : public westmere::i simdjson_inline ~simdjson_result() noexcept = default; ///< @private simdjson_inline simdjson_result raw() const noexcept; + simdjson_inline char operator[](size_t) const noexcept; simdjson_inline simdjson_warn_unused simdjson_result unescape(westmere::ondemand::json_iterator &iter, bool allow_replacement) const noexcept; simdjson_inline simdjson_warn_unused simdjson_result unescape_wobbly(westmere::ondemand::json_iterator &iter) const noexcept; }; - } // namespace simdjson #endif // SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_H @@ -92226,6 +104906,7 @@ struct simdjson_result : public westmere::i /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ #include +#include namespace simdjson { namespace westmere { @@ -92344,7 +105025,9 @@ class parser { simdjson_warn_unused simdjson_result iterate(std::string_view json, size_t capacity) & noexcept; /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ simdjson_warn_unused simdjson_result iterate(const std::string &json) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + /** @overload simdjson_result iterate(padded_string_view json) & noexcept + The string instance might be have its capacity extended. Note that this can still + result in AddressSanitizer: container-overflow in some cases. */ simdjson_warn_unused simdjson_result iterate(std::string &json) & noexcept; /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ simdjson_warn_unused simdjson_result iterate(const simdjson_result &json) & noexcept; @@ -92432,6 +105115,11 @@ class parser { * Setting batch_size to excessively large or excessively small values may impact negatively the * performance. * + * ### Threads + * + * When compiled with SIMDJSON_THREADS_ENABLED, this method will use a single thread under the + * hood to do some lookahead. + * * ### REQUIRED: Buffer Padding * * The buffer must have at least SIMDJSON_PADDING extra allocated bytes. It does not matter what @@ -92439,10 +105127,10 @@ class parser { * using a sanitizer that verifies that no uninitialized byte is read, then you should initialize the * SIMDJSON_PADDING bytes to avoid runtime warnings. * - * ### Threads + * This is checked automatically with all iterate_many function calls, except for the two + * that take pointers (const char* or const uint8_t*). * - * When compiled with SIMDJSON_THREADS_ENABLED, this method will use a single thread under the - * hood to do some lookahead. + * ### Threads * * ### Parser Capacity * @@ -92468,14 +105156,16 @@ class parser { */ inline simdjson_result iterate_many(const uint8_t *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ + inline simdjson_result iterate_many(padded_string_view json, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ inline simdjson_result iterate_many(const char *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ inline simdjson_result iterate_many(const std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; - inline simdjson_result iterate_many(const std::string &&s, size_t batch_size, bool allow_comma_separated = false) = delete;// unsafe + /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) + the string might be automatically padded with up to SIMDJSON_PADDING whitespace characters */ + inline simdjson_result iterate_many(std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ inline simdjson_result iterate_many(const padded_string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; - inline simdjson_result iterate_many(const padded_string &&s, size_t batch_size, bool allow_comma_separated = false) = delete;// unsafe - /** @private We do not want to allow implicit conversion from C string to std::string. */ simdjson_result iterate_many(const char *buf, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept = delete; @@ -92577,13 +105267,39 @@ class parser { bool string_buffer_overflow(const uint8_t *string_buf_loc) const noexcept; #endif + /** + * Get a unique parser instance corresponding to the current thread. + * This instance can be safely used within the current thread, but it should + * not be passed to other threads. + * + * A parser should only be used for one document at a time. + * + * Our simdjson::from functions use this parser instance. + * + * You can free the related parser by calling release_parser(). + */ + static simdjson_inline simdjson_warn_unused ondemand::parser& get_parser(); + /** + * Release the parser instance initialized by get_parser() and all the + * associated resources (memory). Returns true if a parser instance + * was released. + */ + static simdjson_inline bool release_parser(); + private: + friend bool release_parser(); + friend ondemand::parser& get_parser(); + /** Get the thread-local parser instance, allocates it if needed */ + static simdjson_inline simdjson_warn_unused std::unique_ptr& get_parser_instance(); + /** Get the thread-local parser instance, it might be null */ + static simdjson_inline simdjson_warn_unused std::unique_ptr& get_threadlocal_parser_if_exists(); /** @private [for benchmarking access] The implementation to use */ std::unique_ptr implementation{}; size_t _capacity{0}; size_t _max_capacity; size_t _max_depth{DEFAULT_MAX_DEPTH}; std::unique_ptr string_buf{}; + #if SIMDJSON_DEVELOPMENT_CHECKS std::unique_ptr start_positions{}; #endif @@ -92611,6 +105327,315 @@ struct simdjson_result : public westmere::implementa #endif // SIMDJSON_GENERIC_ONDEMAND_PARSER_H /* end file simdjson/generic/ondemand/parser.h for westmere */ +// JSON builder - needed for extract_into functionality +/* including simdjson/generic/ondemand/json_string_builder.h for westmere: #include "simdjson/generic/ondemand/json_string_builder.h" */ +/* begin file simdjson/generic/ondemand/json_string_builder.h for westmere */ +/** + * This file is part of the builder API. It is temporarily in the ondemand directory + * but we will move it to a builder directory later. + */ +#ifndef SIMDJSON_GENERIC_STRING_BUILDER_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { + + +#if SIMDJSON_SUPPORTS_CONCEPTS + +namespace westmere { +namespace builder { + class string_builder; +}} + +template +struct has_custom_serialization : std::false_type {}; + +inline constexpr struct serialize_tag { + template + requires custom_deserializable + constexpr void operator()(westmere::builder::string_builder& b, T& obj) const{ + return tag_invoke(*this, b, obj); + } + + +} serialize{}; +template +struct has_custom_serialization(), std::declval())) +>> : std::true_type {}; + +template +constexpr bool require_custom_serialization = has_custom_serialization::value; +#else +struct has_custom_serialization : std::false_type {}; +#endif // SIMDJSON_SUPPORTS_CONCEPTS + +namespace westmere { +namespace builder { +/** + * A builder for JSON strings representing documents. This is a low-level + * builder that is not meant to be used directly by end-users. Though it + * supports atomic types (Booleans, strings), it does not support composed + * types (arrays and objects). + * + * Ultimately, this class can support kernel-specific optimizations. E.g., + * it may make use of SIMD instructions to escape strings faster. + */ +class string_builder { +public: + simdjson_inline string_builder(size_t initial_capacity = DEFAULT_INITIAL_CAPACITY); + + static constexpr size_t DEFAULT_INITIAL_CAPACITY = 1024; + + /** + * Append number (includes Booleans). Booleans are mapped to the strings + * false and true. Numbers are converted to strings abiding by the JSON standard. + * Floating-point numbers are converted to the shortest string that 'correctly' + * represents the number. + */ + template::value>::type> + simdjson_inline void append(number_type v) noexcept; + + /** + * Append character c. + */ + simdjson_inline void append(char c) noexcept; + + /** + * Append the string 'null'. + */ + simdjson_inline void append_null() noexcept; + + /** + * Clear the content. + */ + simdjson_inline void clear() noexcept; + + /** + * Append the std::string_view, after escaping it. + * There is no UTF-8 validation. + */ + simdjson_inline void escape_and_append(std::string_view input) noexcept; + + /** + * Append the std::string_view surrounded by double quotes, after escaping it. + * There is no UTF-8 validation. + */ + simdjson_inline void escape_and_append_with_quotes(std::string_view input) noexcept; +#if SIMDJSON_SUPPORTS_CONCEPTS + template + simdjson_inline void escape_and_append_with_quotes() noexcept; +#endif + /** + * Append the character surrounded by double quotes, after escaping it. + * There is no UTF-8 validation. + */ + simdjson_inline void escape_and_append_with_quotes(char input) noexcept; + + /** + * Append the character surrounded by double quotes, after escaping it. + * There is no UTF-8 validation. + */ + simdjson_inline void escape_and_append_with_quotes(const char* input) noexcept; + + /** + * Append the C string directly, without escaping. + * There is no UTF-8 validation. + */ + simdjson_inline void append_raw(const char *c) noexcept; + + /** + * Append "{" to the buffer. + */ + simdjson_inline void start_object() noexcept; + + /** + * Append "}" to the buffer. + */ + simdjson_inline void end_object() noexcept; + + /** + * Append "[" to the buffer. + */ + simdjson_inline void start_array() noexcept; + + /** + * Append "]" to the buffer. + */ + simdjson_inline void end_array() noexcept; + + /** + * Append "," to the buffer. + */ + simdjson_inline void append_comma() noexcept; + + /** + * Append ":" to the buffer. + */ + simdjson_inline void append_colon() noexcept; + + /** + * Append a key-value pair to the buffer. + * The key is escaped and surrounded by double quotes. + * The value is escaped if it is a string. + */ + template + simdjson_inline void append_key_value(key_type key, value_type value) noexcept; +#if SIMDJSON_SUPPORTS_CONCEPTS + template + simdjson_inline void append_key_value(value_type value) noexcept; + + // Support for optional types (std::optional, etc.) + template + requires(!require_custom_serialization) + simdjson_inline void append(const T &opt); + + template + requires(require_custom_serialization) + simdjson_inline void append(const T &val); + + // Support for string-like types + template + requires(std::is_convertible::value || + std::is_same::value ) + simdjson_inline void append(const T &value); +#endif +#if SIMDJSON_SUPPORTS_RANGES && SIMDJSON_SUPPORTS_CONCEPTS + // Support for range-based appending (std::ranges::view, etc.) + template +requires (!std::is_convertible::value) + simdjson_inline void append(const R &range) noexcept; +#endif + /** + * Append the std::string_view directly, without escaping. + * There is no UTF-8 validation. + */ + simdjson_inline void append_raw(std::string_view input) noexcept; + + /** + * Append len characters from str. + * There is no UTF-8 validation. + */ + simdjson_inline void append_raw(const char *str, size_t len) noexcept; +#if SIMDJSON_EXCEPTIONS + /** + * Creates an std::string from the written JSON buffer. + * Throws if memory allocation failed + * + * The result may not be valid UTF-8 if some of your content was not valid UTF-8. + * Use validate_unicode() to check the content if needed. + */ + simdjson_inline operator std::string() const noexcept(false); + + /** + * Creates an std::string_view from the written JSON buffer. + * Throws if memory allocation failed. + * + * The result may not be valid UTF-8 if some of your content was not valid UTF-8. + * Use validate_unicode() to check the content if needed. + */ + simdjson_inline operator std::string_view() const noexcept(false) simdjson_lifetime_bound; +#endif + + /** + * Returns a view on the written JSON buffer. Returns an error + * if memory allocation failed. + * + * The result may not be valid UTF-8 if some of your content was not valid UTF-8. + * Use validate_unicode() to check the content. + */ + simdjson_inline simdjson_result view() const noexcept; + + /** + * Appends the null character to the buffer and returns + * a pointer to the beginning of the written JSON buffer. + * Returns an error if memory allocation failed. + * The result is null-terminated. + * + * The result may not be valid UTF-8 if some of your content was not valid UTF-8. + * Use validate_unicode() to check the content. + */ + simdjson_inline simdjson_result c_str() noexcept; + + /** + * Return true if the content is valid UTF-8. + */ + simdjson_inline bool validate_unicode() const noexcept; + + /** + * Returns the current size of the written JSON buffer. + * If an error occurred, returns 0. + */ + simdjson_inline size_t size() const noexcept; + +private: + /** + * Returns true if we can write at least upcoming_bytes bytes. + * The underlying buffer is reallocated if needed. It is designed + * to be called before writing to the buffer. It should be fast. + */ + simdjson_inline bool capacity_check(size_t upcoming_bytes); + + /** + * Grow the buffer to at least desired_capacity bytes. + * If the allocation fails, is_valid is set to false. We expect + * that this function would not be repeatedly called. + */ + simdjson_inline void grow_buffer(size_t desired_capacity); + + /** + * We use this helper function to make sure that is_valid is kept consistent. + */ + simdjson_inline void set_valid(bool valid) noexcept; + + std::unique_ptr buffer{}; + size_t position{0}; + size_t capacity{0}; + bool is_valid{true}; +}; + + + +} +} + + +#if !SIMDJSON_STATIC_REFLECTION +// fallback implementation until we have static reflection +template +simdjson_warn_unused simdjson_result to_json(const Z &z, size_t initial_capacity = simdjson::westmere::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { + simdjson::westmere::builder::string_builder b(initial_capacity); + b.append(z); + std::string_view s; + auto e = b.view().get(s); + if(e) { return e; } + return std::string(s); +} +template +simdjson_warn_unused simdjson_error to_json(const Z &z, std::string &s, size_t initial_capacity = simdjson::westmere::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { + simdjson::westmere::builder::string_builder b(initial_capacity); + b.append(z); + std::string_view sv; + auto e = b.view().get(sv); + if(e) { return e; } + s.assign(sv.data(), sv.size()); + return simdjson::SUCCESS; +} +#endif + +#if SIMDJSON_SUPPORTS_CONCEPTS +#endif // SIMDJSON_SUPPORTS_CONCEPTS + +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_STRING_BUILDER_H +/* end file simdjson/generic/ondemand/json_string_builder.h for westmere */ + // All other declarations /* including simdjson/generic/ondemand/array.h for westmere: #include "simdjson/generic/ondemand/array.h" */ /* begin file simdjson/generic/ondemand/array.h for westmere */ @@ -92747,11 +105772,42 @@ class array { * - INDEX_OUT_OF_BOUNDS if the array index is larger than an array length */ simdjson_inline simdjson_result at(size_t index) noexcept; + +#if SIMDJSON_SUPPORTS_CONCEPTS + /** + * Get this array as the given type. + * + * @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized. + * @returns INCORRECT_TYPE If the JSON array is not of the given type. + * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. + */ + template + simdjson_warn_unused simdjson_inline error_code get(T &out) + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) { + static_assert(custom_deserializable); + return deserialize(*this, out); + } + /** + * Get this array as the given type. + * + * @returns A value of the given type, parsed from the JSON. + * @returns INCORRECT_TYPE If the JSON value is not the given type. + */ + template + simdjson_inline simdjson_result get() + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) + { + static_assert(std::is_default_constructible::value, "The specified type is not default constructible."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } +#endif // SIMDJSON_SUPPORTS_CONCEPTS protected: /** * Go to the end of the array, no matter where you are right now. */ - simdjson_inline error_code consume() noexcept; + simdjson_warn_unused simdjson_inline error_code consume() noexcept; /** * Begin array iteration. @@ -92825,7 +105881,28 @@ struct simdjson_result : public westmere::implementat simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; simdjson_inline simdjson_result raw_json() noexcept; +#if SIMDJSON_SUPPORTS_CONCEPTS + // TODO: move this code into object-inl.h + template + simdjson_inline simdjson_result get() noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + return first; + } + return first.get(); + } + template + simdjson_warn_unused simdjson_inline error_code get(T& out) noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + out = first; + } else { + SIMDJSON_TRY( first.get(out) ); + } + return SUCCESS; + } +#endif // SIMDJSON_SUPPORTS_CONCEPTS }; } // namespace simdjson @@ -92870,7 +105947,8 @@ class array_iterator { * * Part of the std::iterator interface. */ - simdjson_inline simdjson_result operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. + simdjson_inline simdjson_result + operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. /** * Check if we are at the end of the JSON. * @@ -92894,6 +105972,11 @@ class array_iterator { */ simdjson_inline array_iterator &operator++() noexcept; + /** + * Check if the array is at the end. + */ + simdjson_warn_unused simdjson_inline bool at_end() const noexcept; + private: value_iterator iter{}; @@ -92912,7 +105995,6 @@ namespace simdjson { template<> struct simdjson_result : public westmere::implementation_simdjson_result_base { -public: simdjson_inline simdjson_result(westmere::ondemand::array_iterator &&value) noexcept; ///< @private simdjson_inline simdjson_result(error_code error) noexcept; ///< @private simdjson_inline simdjson_result() noexcept = default; @@ -92925,6 +106007,8 @@ struct simdjson_result : public westmere::im simdjson_inline bool operator==(const simdjson_result &) const noexcept; simdjson_inline bool operator!=(const simdjson_result &) const noexcept; simdjson_inline simdjson_result &operator++() noexcept; + + simdjson_warn_unused simdjson_inline bool at_end() const noexcept; }; } // namespace simdjson @@ -93052,7 +106136,7 @@ class document { * @returns INCORRECT_TYPE if the JSON value is not a string. Otherwise, we return SUCCESS. */ template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; /** * Cast this JSON value to a string. * @@ -93118,7 +106202,7 @@ class document { */ template simdjson_inline simdjson_result get() & -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept @@ -93141,7 +106225,7 @@ class document { */ template simdjson_inline simdjson_result get() && -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept @@ -93159,18 +106243,18 @@ class document { * Be mindful that the document instance must remain in scope while you are accessing object, array and value instances. * * @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized. - * @returns INCORRECT_TYPE If the JSON value is not an object. + * @returns INCORRECT_TYPE If the JSON value is of the given type. * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. */ template - simdjson_inline error_code get(T &out) & -#if SIMDJSON_SUPPORTS_DESERIALIZATION + simdjson_warn_unused simdjson_inline error_code get(T &out) & +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept #endif { -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS if constexpr (custom_deserializable) { return deserialize(*this, out); } else { @@ -93182,7 +106266,7 @@ class document { static_cast(out); // to get rid of unused errors return UNINITIALIZED; } -#else // SIMDJSON_SUPPORTS_DESERIALIZATION +#else // SIMDJSON_SUPPORTS_CONCEPTS // Unless the simdjson library or the user provides an inline implementation, calling this method should // immediately fail. static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " @@ -93192,7 +106276,7 @@ class document { " You may also add support for custom types, see our documentation."); static_cast(out); // to get rid of unused errors return UNINITIALIZED; -#endif // SIMDJSON_SUPPORTS_DESERIALIZATION +#endif // SIMDJSON_SUPPORTS_CONCEPTS } /** @overload template error_code get(T &out) & noexcept */ @@ -93257,7 +106341,7 @@ class document { * time it parses a document or when it is destroyed. * @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string. */ - simdjson_inline operator std::string_view() noexcept(false); + simdjson_inline operator std::string_view() noexcept(false) simdjson_lifetime_bound; /** * Cast this JSON value to a raw_json_string. * @@ -93266,7 +106350,7 @@ class document { * @returns A pointer to the raw JSON for the given string. * @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string. */ - simdjson_inline operator raw_json_string() noexcept(false); + simdjson_inline operator raw_json_string() noexcept(false) simdjson_lifetime_bound; /** * Cast this JSON value to a bool. * @@ -93416,11 +106500,27 @@ class document { * E.g., you must still call "is_null()" to check that a value is null even if * "type()" returns json_type::null. * + * The answer can be one of + * simdjson::ondemand::json_type::object, + * simdjson::ondemand::json_type::array, + * simdjson::ondemand::json_type::string, + * simdjson::ondemand::json_type::number, + * simdjson::ondemand::json_type::boolean, + * simdjson::ondemand::json_type::null. + * + * Starting with simdjson 4.0, this function will return simdjson::ondemand::json_type::unknown + * given a bad token. + * This allows you to identify a case such as {"key": NaN} and identify the NaN value. + * The simdjson::ondemand::json_type::unknown value should only happen with non-valid JSON. + * * NOTE: If you're only expecting a value to be one type (a typical case), it's generally * better to just call .get_double, .get_string, etc. and check for INCORRECT_TYPE (or just * let it throw an exception). * - * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". + * Prior to simdjson 4.0, this function would return an error given a bad token. + * Starting with simdjson 4.0, it will return simdjson::ondemand::json_type::unknown. + * This allows you to identify a case such as {"key": NaN} and identify the NaN value. + * The simdjson::ondemand::json_type::unknown value should only happen with non-valid JSON. */ simdjson_inline simdjson_result type() noexcept; @@ -93644,11 +106744,41 @@ class document { * the JSON document. */ simdjson_inline simdjson_result raw_json() noexcept; + +#if SIMDJSON_STATIC_REFLECTION + /** + * Extract only specific fields from the JSON object into a struct. + * + * This allows selective deserialization of only the fields you need, + * potentially improving performance by skipping unwanted fields. + * + * Example: + * ```c++ + * struct Car { + * std::string make; + * std::string model; + * int year; + * double price; + * }; + * + * Car car; + * doc.extract_into<"make", "model">(car); + * // Only 'make' and 'model' fields are extracted from JSON + * ``` + * + * @tparam FieldNames Compile-time string literals specifying which fields to extract + * @param out The output struct to populate with selected fields + * @returns SUCCESS on success, or an error code if a required field is missing or has wrong type + */ + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept; +#endif // SIMDJSON_STATIC_REFLECTION protected: /** * Consumes the document. */ - simdjson_inline error_code consume() noexcept; + simdjson_warn_unused simdjson_inline error_code consume() noexcept; simdjson_inline document(ondemand::json_iterator &&iter) noexcept; simdjson_inline const uint8_t *text(uint32_t idx) const noexcept; @@ -93701,7 +106831,7 @@ class document_reference { simdjson_inline simdjson_result get_double_in_string() noexcept; simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; simdjson_inline simdjson_result get_wobbly_string() noexcept; simdjson_inline simdjson_result get_raw_json_string() noexcept; simdjson_inline simdjson_result get_bool() noexcept; @@ -93710,7 +106840,7 @@ class document_reference { simdjson_inline simdjson_result is_null() noexcept; template simdjson_inline simdjson_result get() & -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept @@ -93723,7 +106853,7 @@ class document_reference { } template simdjson_inline simdjson_result get() && -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept @@ -93745,14 +106875,14 @@ class document_reference { * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. */ template - simdjson_inline error_code get(T &out) & -#if SIMDJSON_SUPPORTS_DESERIALIZATION + simdjson_warn_unused simdjson_inline error_code get(T &out) & +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept #endif { -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS if constexpr (custom_deserializable) { return deserialize(*this, out); } else { @@ -93764,7 +106894,7 @@ class document_reference { static_cast(out); // to get rid of unused errors return UNINITIALIZED; } -#else // SIMDJSON_SUPPORTS_DESERIALIZATION +#else // SIMDJSON_SUPPORTS_CONCEPTS // Unless the simdjson library or the user provides an inline implementation, calling this method should // immediately fail. static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " @@ -93774,12 +106904,17 @@ class document_reference { " You may also add support for custom types, see our documentation."); static_cast(out); // to get rid of unused errors return UNINITIALIZED; -#endif // SIMDJSON_SUPPORTS_DESERIALIZATION +#endif // SIMDJSON_SUPPORTS_CONCEPTS } /** @overload template error_code get(T &out) & noexcept */ template simdjson_inline error_code get(T &out) && noexcept; simdjson_inline simdjson_result raw_json() noexcept; +#if SIMDJSON_STATIC_REFLECTION + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept; +#endif // SIMDJSON_STATIC_REFLECTION simdjson_inline operator document&() const noexcept; #if SIMDJSON_EXCEPTIONS template @@ -93848,7 +106983,7 @@ struct simdjson_result : public westmere::implemen simdjson_inline simdjson_result get_double_in_string() noexcept; simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; simdjson_inline simdjson_result get_wobbly_string() noexcept; simdjson_inline simdjson_result get_raw_json_string() noexcept; simdjson_inline simdjson_result get_bool() noexcept; @@ -93861,6 +106996,9 @@ struct simdjson_result : public westmere::implemen template simdjson_inline error_code get(T &out) & noexcept; template simdjson_inline error_code get(T &out) && noexcept; #if SIMDJSON_EXCEPTIONS + + using westmere::implementation_simdjson_result_base::operator*; + using westmere::implementation_simdjson_result_base::operator->; template ::value == false>::type> explicit simdjson_inline operator T() noexcept(false); simdjson_inline operator westmere::ondemand::array() & noexcept(false); @@ -93900,6 +107038,11 @@ struct simdjson_result : public westmere::implemen simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; +#if SIMDJSON_STATIC_REFLECTION + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept; +#endif // SIMDJSON_STATIC_REFLECTION }; @@ -93926,7 +107069,7 @@ struct simdjson_result : public westmere simdjson_inline simdjson_result get_double_in_string() noexcept; simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; simdjson_inline simdjson_result get_wobbly_string() noexcept; simdjson_inline simdjson_result get_raw_json_string() noexcept; simdjson_inline simdjson_result get_bool() noexcept; @@ -93977,6 +107120,11 @@ struct simdjson_result : public westmere simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; +#if SIMDJSON_STATIC_REFLECTION + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept; +#endif // SIMDJSON_STATIC_REFLECTION }; @@ -94119,6 +107267,7 @@ class document_stream { * Default constructor. */ simdjson_inline iterator() noexcept; + simdjson_inline iterator(const iterator &other) noexcept = default; /** * Get the current document (or error). */ @@ -94132,6 +107281,7 @@ class document_stream { * @param other the end iterator to compare to. */ simdjson_inline bool operator!=(const iterator &other) const noexcept; + simdjson_inline bool operator==(const iterator &other) const noexcept; /** * @private * @@ -94175,6 +107325,11 @@ class document_stream { */ inline error_code error() const noexcept; + /** + * Returns whether the iterator is at the end. + */ + inline bool at_end() const noexcept; + private: simdjson_inline iterator(document_stream *s, bool finished) noexcept; /** The document_stream we're iterating through. */ @@ -94186,6 +107341,7 @@ class document_stream { friend class document_stream; friend class json_iterator; }; + using iterator = document_stream::iterator; /** * Start iterating the documents in the stream. @@ -94449,6 +107605,9 @@ struct simdjson_result : public westmere::implementat /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */ +/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION && SIMDJSON_SUPPORTS_CONCEPTS */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_string_builder.h" // for constevalutil::fixed_string */ +/* amalgamation skipped (editor-only): #endif */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { @@ -94646,11 +107805,71 @@ class object { */ simdjson_inline simdjson_result raw_json() noexcept; +#if SIMDJSON_SUPPORTS_CONCEPTS + /** + * Get this object as the given type. + * + * @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized. + * @returns INCORRECT_TYPE If the JSON object is not of the given type. + * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. + */ + template + simdjson_warn_unused simdjson_inline error_code get(T &out) + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) { + static_assert(custom_deserializable); + return deserialize(*this, out); + } + /** + * Get this array as the given type. + * + * @returns A value of the given type, parsed from the JSON. + * @returns INCORRECT_TYPE If the JSON value is not the given type. + */ + template + simdjson_inline simdjson_result get() + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) + { + static_assert(std::is_default_constructible::value, "The specified type is not default constructible."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } + +#if SIMDJSON_STATIC_REFLECTION + /** + * Extract only specific fields from the JSON object into a struct. + * + * This allows selective deserialization of only the fields you need, + * potentially improving performance by skipping unwanted fields. + * + * Example: + * ```c++ + * struct Car { + * std::string make; + * std::string model; + * int year; + * double price; + * }; + * + * Car car; + * object.extract_into<"make", "model">(car); + * // Only 'make' and 'model' fields are extracted from JSON + * ``` + * + * @tparam FieldNames Compile-time string literals specifying which fields to extract + * @param out The output struct to populate with selected fields + * @returns SUCCESS on success, or an error code if a required field is missing or has wrong type + */ + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept; +#endif // SIMDJSON_STATIC_REFLECTION +#endif // SIMDJSON_SUPPORTS_CONCEPTS protected: /** * Go to the end of the object, no matter where you are right now. */ - simdjson_inline error_code consume() noexcept; + simdjson_warn_unused simdjson_inline error_code consume() noexcept; static simdjson_inline simdjson_result start(value_iterator &iter) noexcept; static simdjson_inline simdjson_result start_root(value_iterator &iter) noexcept; static simdjson_inline simdjson_result started(value_iterator &iter) noexcept; @@ -94689,12 +107908,42 @@ struct simdjson_result : public westmere::implementa simdjson_inline simdjson_result operator[](std::string_view key) && noexcept; simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; - inline simdjson_result reset() noexcept; inline simdjson_result is_empty() noexcept; inline simdjson_result count_fields() & noexcept; inline simdjson_result raw_json() noexcept; + #if SIMDJSON_SUPPORTS_CONCEPTS + // TODO: move this code into object-inl.h + template + simdjson_inline simdjson_result get() noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + return first; + } + return first.get(); + } + template + simdjson_warn_unused simdjson_inline error_code get(T& out) noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + out = first; + } else { + SIMDJSON_TRY( first.get(out) ); + } + return SUCCESS; + } + +#if SIMDJSON_STATIC_REFLECTION + // TODO: move this code into object-inl.h + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) noexcept { + if (error()) { return error(); } + return first.extract_into(out); + } +#endif // SIMDJSON_STATIC_REFLECTION +#endif // SIMDJSON_SUPPORTS_CONCEPTS }; } // namespace simdjson @@ -94823,6 +108072,20 @@ inline simdjson_result to_json_string(simdjson_result to_json_string(simdjson_result x); inline simdjson_result to_json_string(simdjson_result x); inline simdjson_result to_json_string(simdjson_result x); + +#if SIMDJSON_STATIC_REFLECTION +/** + * Create a JSON string from any user-defined type using static reflection. + * Only available when SIMDJSON_STATIC_REFLECTION is enabled. + */ +template + requires(!std::same_as && + !std::same_as && + !std::same_as && + !std::same_as) +inline std::string to_json_string(const T& obj); +#endif + } // namespace simdjson /** @@ -94894,28 +108157,30 @@ inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result #include +#if SIMDJSON_STATIC_REFLECTION +#include +// #include // for std::define_static_string - header not available yet +#endif namespace simdjson { -template -constexpr bool require_custom_serialization = false; ////////////////////////////// // Number deserialization ////////////////////////////// template - requires(!require_custom_serialization) error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { using limits = std::numeric_limits; @@ -94929,7 +108194,6 @@ error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { } template - requires(!require_custom_serialization) error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { double x; SIMDJSON_TRY(val.get_double().get(x)); @@ -94938,7 +108202,6 @@ error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { } template - requires(!require_custom_serialization) error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { using limits = std::numeric_limits; @@ -94951,8 +108214,23 @@ error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { return SUCCESS; } +////////////////////////////// +// String deserialization +////////////////////////////// + +// just a character! +error_code tag_invoke(deserialize_tag, auto &val, char &out) noexcept { + std::string_view x; + SIMDJSON_TRY(val.get_string().get(x)); + if(x.size() != 1) { + return INCORRECT_TYPE; + } + out = x[0]; + return SUCCESS; +} + +// any string-like type (can be constructed from std::string_view) template - requires(!require_custom_serialization) error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(std::is_nothrow_constructible_v) { std::string_view str; SIMDJSON_TRY(val.get_string().get(str)); @@ -94969,7 +108247,6 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(std::is_nothr * doc.get>(). */ template - requires(!require_custom_serialization) error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { using value_type = typename std::remove_cvref_t::value_type; static_assert( @@ -94978,9 +108255,13 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { static_assert( std::is_default_constructible_v, "The specified type inside the container must default constructible."); - westmere::ondemand::array arr; - SIMDJSON_TRY(val.get_array().get(arr)); + if constexpr (std::is_same_v, westmere::ondemand::array>) { + arr = val; + } else { + SIMDJSON_TRY(val.get_array().get(arr)); + } + for (auto v : arr) { if constexpr (concepts::returns_reference) { if (auto const err = v.get().get(concepts::emplace_one(out)); @@ -95011,7 +108292,6 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { * string-keyed types. */ template - requires(!require_custom_serialization) error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { using value_type = typename std::remove_cvref_t::mapped_type; static_assert( @@ -95037,7 +108317,45 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { return SUCCESS; } +template +error_code tag_invoke(deserialize_tag, westmere::ondemand::object &obj, T &out) noexcept { + using value_type = typename std::remove_cvref_t::mapped_type; + out.clear(); + for (auto field : obj) { + std::string_view key; + SIMDJSON_TRY(field.unescaped_key().get(key)); + + westmere::ondemand::value value_obj; + SIMDJSON_TRY(field.value().get(value_obj)); + + value_type this_value; + SIMDJSON_TRY(value_obj.get(this_value)); + out.emplace(typename T::key_type(key), std::move(this_value)); + } + return SUCCESS; +} + +template +error_code tag_invoke(deserialize_tag, westmere::ondemand::value &val, T &out) noexcept { + westmere::ondemand::object obj; + SIMDJSON_TRY(val.get_object().get(obj)); + return simdjson::deserialize(obj, out); +} + +template +error_code tag_invoke(deserialize_tag, westmere::ondemand::document &doc, T &out) noexcept { + westmere::ondemand::object obj; + SIMDJSON_TRY(doc.get_object().get(obj)); + return simdjson::deserialize(obj, out); +} + +template +error_code tag_invoke(deserialize_tag, westmere::ondemand::document_reference &doc, T &out) noexcept { + westmere::ondemand::object obj; + SIMDJSON_TRY(doc.get_object().get(obj)); + return simdjson::deserialize(obj, out); +} /** @@ -95055,7 +108373,6 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { * @return status of the conversion */ template - requires(!require_custom_serialization) error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deserializable::element_type, ValT>) { using element_type = typename std::remove_cvref_t::element_type; @@ -95080,17 +108397,17 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deser /** * This CPO (Customization Point Object) will help deserialize into optional types. */ -template - requires(!require_custom_serialization) -error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deserializable::value_type, ValT>) { +template +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept(nothrow_deserializable::value_type, decltype(val)>) { using value_type = typename std::remove_cvref_t::value_type; - static_assert( - deserializable, - "The specified type inside the unique_ptr must itself be deserializable"); - static_assert( - std::is_default_constructible_v, - "The specified type inside the unique_ptr must default constructible."); + // Check if the value is null + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); // Set to nullopt + return SUCCESS; + } if (!out) { out.emplace(); @@ -95099,10 +108416,329 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deser return SUCCESS; } + +#if SIMDJSON_STATIC_REFLECTION + + +template +constexpr bool user_defined_type = (std::is_class_v +&& !std::is_same_v && !std::is_same_v && !concepts::optional_type && +!concepts::appendable_containers); + + +template + requires(user_defined_type && std::is_class_v) +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept { + westmere::ondemand::object obj; + if constexpr (std::is_same_v, westmere::ondemand::object>) { + obj = val; + } else { + SIMDJSON_TRY(val.get_object().get(obj)); + } + template for (constexpr auto mem : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) { + if constexpr (!std::meta::is_const(mem) && std::meta::is_public(mem)) { + constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem)); + if constexpr (concepts::optional_type) { + // for optional members, it's ok if the key is missing + auto error = obj[key].get(out.[:mem:]); + if (error && error != NO_SUCH_FIELD) { + if(error == NO_SUCH_FIELD) { + out.[:mem:].reset(); + continue; + } + return error; + } + } else { + // for non-optional members, the key must be present + SIMDJSON_TRY(obj[key].get(out.[:mem:])); + } + } + }; + return simdjson::SUCCESS; +} + +// Support for enum deserialization - deserialize from string representation using expand approach from P2996R12 +template + requires(std::is_enum_v) +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept { +#if SIMDJSON_STATIC_REFLECTION + std::string_view str; + SIMDJSON_TRY(val.get_string().get(str)); + constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T)); + template for (constexpr auto enum_val : enumerators) { + if (str == std::meta::identifier_of(enum_val)) { + out = [:enum_val:]; + return SUCCESS; + } + }; + + return INCORRECT_TYPE; +#else + // Fallback: deserialize as integer if reflection not available + std::underlying_type_t int_val; + SIMDJSON_TRY(val.get(int_val)); + out = static_cast(int_val); + return SUCCESS; +#endif +} + +template + requires(user_defined_type>) +error_code tag_invoke(deserialize_tag, simdjson_value &val, std::unique_ptr &out) noexcept { + if (!out) { + out = std::make_unique(); + if (!out) { + return MEMALLOC; + } + } + if (auto err = val.get(*out)) { + out.reset(); + return err; + } + return SUCCESS; +} + +template + requires(user_defined_type>) +error_code tag_invoke(deserialize_tag, simdjson_value &val, std::shared_ptr &out) noexcept { + if (!out) { + out = std::make_shared(); + if (!out) { + return MEMALLOC; + } + } + if (auto err = val.get(*out)) { + out.reset(); + return err; + } + return SUCCESS; +} + +#endif // SIMDJSON_STATIC_REFLECTION + +//////////////////////////////////////// +// Unique pointers +//////////////////////////////////////// +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_unique(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_bool().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_unique(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_int64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_unique(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_uint64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_unique(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_double().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_unique(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_string().get(*out)); + return SUCCESS; +} + + +//////////////////////////////////////// +// Shared pointers +//////////////////////////////////////// +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_shared(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_bool().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_shared(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_int64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_shared(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_uint64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_shared(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_double().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_shared(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_string().get(*out)); + return SUCCESS; +} + + +//////////////////////////////////////// +// Explicit optional specializations +//////////////////////////////////////// + +//////////////////////////////////////// +// Explicit smart pointer specializations for string and int types +//////////////////////////////////////// +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + // Check if the value is null + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); // Set to nullptr + return SUCCESS; + } + + if (!out) { + out = std::make_unique(); + } + std::string_view str; + SIMDJSON_TRY(val.get_string().get(str)); + *out = std::string{str}; + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + // Check if the value is null + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); // Set to nullptr + return SUCCESS; + } + + if (!out) { + out = std::make_shared(); + } + std::string_view str; + SIMDJSON_TRY(val.get_string().get(str)); + *out = std::string{str}; + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + // Check if the value is null + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); // Set to nullptr + return SUCCESS; + } + + if (!out) { + out = std::make_unique(); + } + int64_t temp; + SIMDJSON_TRY(val.get_int64().get(temp)); + *out = static_cast(temp); + return SUCCESS; +} + } // namespace simdjson #endif // SIMDJSON_ONDEMAND_DESERIALIZE_H -#endif // SIMDJSON_SUPPORTS_DESERIALIZATION +#endif // SIMDJSON_SUPPORTS_CONCEPTS /* end file simdjson/generic/ondemand/std_deserialize.h for westmere */ // Inline definitions @@ -95195,7 +108831,7 @@ simdjson_inline simdjson_result array::begin() noexcept { simdjson_inline simdjson_result array::end() noexcept { return array_iterator(iter); } -simdjson_inline error_code array::consume() noexcept { +simdjson_warn_unused simdjson_warn_unused simdjson_inline error_code array::consume() noexcept { auto error = iter.json_iter().skip_child(iter.depth()-1); if(error) { iter.abandon(); } return error; @@ -95386,6 +109022,9 @@ simdjson_inline array_iterator &array_iterator::operator++() noexcept { return *this; } +simdjson_inline bool array_iterator::at_end() const noexcept { + return iter.at_end(); +} } // namespace ondemand } // namespace westmere } // namespace simdjson @@ -95422,7 +109061,9 @@ simdjson_inline simdjson_result &simdjson_re ++(first); return *this; } - +simdjson_inline bool simdjson_result::at_end() const noexcept { + return !first.iter.is_valid() || first.at_end(); +} } // namespace simdjson #endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_INL_H @@ -95479,7 +109120,7 @@ simdjson_inline simdjson_result value::get_string(bool allow_r return iter.get_string(allow_replacement); } template -simdjson_inline error_code value::get_string(string_type& receiver, bool allow_replacement) noexcept { +simdjson_warn_unused simdjson_inline error_code value::get_string(string_type& receiver, bool allow_replacement) noexcept { return iter.get_string(receiver, allow_replacement); } simdjson_inline simdjson_result value::get_wobbly_string() noexcept { @@ -95521,15 +109162,15 @@ template<> simdjson_inline simdjson_result value::get() noexcept { retu template<> simdjson_inline simdjson_result value::get() noexcept { return get_bool(); } -template<> simdjson_inline error_code value::get(array& out) noexcept { return get_array().get(out); } -template<> simdjson_inline error_code value::get(object& out) noexcept { return get_object().get(out); } -template<> simdjson_inline error_code value::get(raw_json_string& out) noexcept { return get_raw_json_string().get(out); } -template<> simdjson_inline error_code value::get(std::string_view& out) noexcept { return get_string(false).get(out); } -template<> simdjson_inline error_code value::get(number& out) noexcept { return get_number().get(out); } -template<> simdjson_inline error_code value::get(double& out) noexcept { return get_double().get(out); } -template<> simdjson_inline error_code value::get(uint64_t& out) noexcept { return get_uint64().get(out); } -template<> simdjson_inline error_code value::get(int64_t& out) noexcept { return get_int64().get(out); } -template<> simdjson_inline error_code value::get(bool& out) noexcept { return get_bool().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(array& out) noexcept { return get_array().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(object& out) noexcept { return get_object().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(raw_json_string& out) noexcept { return get_raw_json_string().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(std::string_view& out) noexcept { return get_string(false).get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(number& out) noexcept { return get_number().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(double& out) noexcept { return get_double().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(uint64_t& out) noexcept { return get_uint64().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(int64_t& out) noexcept { return get_int64().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(bool& out) noexcept { return get_bool().get(out); } #if SIMDJSON_EXCEPTIONS template @@ -96126,7 +109767,7 @@ simdjson_inline simdjson_result document::get_string(bool allo return get_root_value_iterator().get_root_string(true, allow_replacement); } template -simdjson_inline error_code document::get_string(string_type& receiver, bool allow_replacement) noexcept { +simdjson_warn_unused simdjson_inline error_code document::get_string(string_type& receiver, bool allow_replacement) noexcept { return get_root_value_iterator().get_root_string(receiver, true, allow_replacement); } simdjson_inline simdjson_result document::get_wobbly_string() noexcept { @@ -96152,15 +109793,15 @@ template<> simdjson_inline simdjson_result document::get() & noexcept { template<> simdjson_inline simdjson_result document::get() & noexcept { return get_bool(); } template<> simdjson_inline simdjson_result document::get() & noexcept { return get_value(); } -template<> simdjson_inline error_code document::get(array& out) & noexcept { return get_array().get(out); } -template<> simdjson_inline error_code document::get(object& out) & noexcept { return get_object().get(out); } -template<> simdjson_inline error_code document::get(raw_json_string& out) & noexcept { return get_raw_json_string().get(out); } -template<> simdjson_inline error_code document::get(std::string_view& out) & noexcept { return get_string(false).get(out); } -template<> simdjson_inline error_code document::get(double& out) & noexcept { return get_double().get(out); } -template<> simdjson_inline error_code document::get(uint64_t& out) & noexcept { return get_uint64().get(out); } -template<> simdjson_inline error_code document::get(int64_t& out) & noexcept { return get_int64().get(out); } -template<> simdjson_inline error_code document::get(bool& out) & noexcept { return get_bool().get(out); } -template<> simdjson_inline error_code document::get(value& out) & noexcept { return get_value().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(array& out) & noexcept { return get_array().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(object& out) & noexcept { return get_object().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(raw_json_string& out) & noexcept { return get_raw_json_string().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(std::string_view& out) & noexcept { return get_string(false).get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(double& out) & noexcept { return get_double().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(uint64_t& out) & noexcept { return get_uint64().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(int64_t& out) & noexcept { return get_int64().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(bool& out) & noexcept { return get_bool().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(value& out) & noexcept { return get_value().get(out); } template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_raw_json_string(); } template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_string(false); } @@ -96180,8 +109821,8 @@ simdjson_inline document::operator object() & noexcept(false) { return get_objec simdjson_inline document::operator uint64_t() noexcept(false) { return get_uint64(); } simdjson_inline document::operator int64_t() noexcept(false) { return get_int64(); } simdjson_inline document::operator double() noexcept(false) { return get_double(); } -simdjson_inline document::operator std::string_view() noexcept(false) { return get_string(false); } -simdjson_inline document::operator raw_json_string() noexcept(false) { return get_raw_json_string(); } +simdjson_inline document::operator std::string_view() noexcept(false) simdjson_lifetime_bound { return get_string(false); } +simdjson_inline document::operator raw_json_string() noexcept(false) simdjson_lifetime_bound { return get_raw_json_string(); } simdjson_inline document::operator bool() noexcept(false) { return get_bool(); } simdjson_inline document::operator value() noexcept(false) { return get_value(); } @@ -96230,7 +109871,7 @@ simdjson_inline simdjson_result document::operator[](const char *key) & n return start_or_resume_object()[key]; } -simdjson_inline error_code document::consume() noexcept { +simdjson_warn_unused simdjson_inline error_code document::consume() noexcept { bool scalar = false; auto error = is_scalar().get(scalar); if(error) { return error; } @@ -96332,6 +109973,54 @@ simdjson_inline simdjson_result document::at_path(std::string_view json_p } } + + +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION + +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code document::extract_into(T& out) & noexcept { + // Helper to check if a field name matches any of the requested fields + auto should_extract = [](std::string_view field_name) constexpr -> bool { + return ((FieldNames.view() == field_name) || ...); + }; + + // Iterate through all members of T using reflection + template for (constexpr auto mem : std::define_static_array( + std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) { + + if constexpr (!std::meta::is_const(mem) && std::meta::is_public(mem)) { + constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem)); + + // Only extract this field if it's in our list of requested fields + if constexpr (should_extract(key)) { + // Try to find and extract the field + if constexpr (concepts::optional_type) { + // For optional fields, it's ok if they're missing + auto field_result = find_field_unordered(key); + if (!field_result.error()) { + auto error = field_result.get(out.[:mem:]); + if (error && error != NO_SUCH_FIELD) { + return error; + } + } else if (field_result.error() != NO_SUCH_FIELD) { + return field_result.error(); + } else { + out.[:mem:].reset(); + } + } else { + // For required fields (in the requested list), fail if missing + SIMDJSON_TRY((*this)[key].get(out.[:mem:])); + } + } + } + }; + + return SUCCESS; +} + +#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION + } // namespace ondemand } // namespace westmere } // namespace simdjson @@ -96439,7 +110128,7 @@ simdjson_inline simdjson_result simdjson_result -simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { if (error()) { return error(); } return first.get_string(receiver, allow_replacement); } @@ -96475,12 +110164,12 @@ simdjson_deprecated simdjson_inline simdjson_result simdjson_result(first).get(); } template -simdjson_inline error_code simdjson_result::get(T &out) & noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) & noexcept { if (error()) { return error(); } return first.get(out); } template -simdjson_inline error_code simdjson_result::get(T &out) && noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) && noexcept { if (error()) { return error(); } return std::forward(first).get(out); } @@ -96490,8 +110179,8 @@ template<> simdjson_deprecated simdjson_inline simdjson_result(first); } -template<> simdjson_inline error_code simdjson_result::get(westmere::ondemand::document &out) & noexcept = delete; -template<> simdjson_inline error_code simdjson_result::get(westmere::ondemand::document &out) && noexcept { +template<> simdjson_warn_unused simdjson_inline error_code simdjson_result::get(westmere::ondemand::document &out) & noexcept = delete; +template<> simdjson_warn_unused simdjson_inline error_code simdjson_result::get(westmere::ondemand::document &out) && noexcept { if (error()) { return error(); } out = std::forward(first); return SUCCESS; @@ -96609,6 +110298,15 @@ simdjson_inline simdjson_result simdjson_result + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code simdjson_result::extract_into(T& out) & noexcept { + if (error()) { return error(); } + return first.extract_into(out); +} +#endif // SIMDJSON_STATIC_REFLECTION + } // namespace simdjson @@ -96643,7 +110341,7 @@ simdjson_inline simdjson_result document_reference::get_double() noexcep simdjson_inline simdjson_result document_reference::get_double_in_string() noexcept { return doc->get_root_value_iterator().get_root_double(false); } simdjson_inline simdjson_result document_reference::get_string(bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(false, allow_replacement); } template -simdjson_inline error_code document_reference::get_string(string_type& receiver, bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(receiver, false, allow_replacement); } +simdjson_warn_unused simdjson_inline error_code document_reference::get_string(string_type& receiver, bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(receiver, false, allow_replacement); } simdjson_inline simdjson_result document_reference::get_wobbly_string() noexcept { return doc->get_root_value_iterator().get_root_wobbly_string(false); } simdjson_inline simdjson_result document_reference::get_raw_json_string() noexcept { return doc->get_root_value_iterator().get_root_raw_json_string(false); } simdjson_inline simdjson_result document_reference::get_bool() noexcept { return doc->get_root_value_iterator().get_root_bool(false); } @@ -96696,7 +110394,13 @@ simdjson_inline simdjson_result document_reference::at_pointer(std::strin simdjson_inline simdjson_result document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); } simdjson_inline simdjson_result document_reference::raw_json() noexcept { return doc->raw_json();} simdjson_inline document_reference::operator document&() const noexcept { return *doc; } - +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code document_reference::extract_into(T& out) & noexcept { + return doc->extract_into(out); +} +#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION } // namespace ondemand } // namespace westmere } // namespace simdjson @@ -96793,7 +110497,7 @@ simdjson_inline simdjson_result simdjson_result -simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { if (error()) { return error(); } return first.get_string(receiver, allow_replacement); } @@ -96828,12 +110532,12 @@ simdjson_inline simdjson_result simdjson_result(first).get(); } template -simdjson_inline error_code simdjson_result::get(T &out) & noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) & noexcept { if (error()) { return error(); } return first.get(out); } template -simdjson_inline error_code simdjson_result::get(T &out) && noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) && noexcept { if (error()) { return error(); } return std::forward(first).get(out); } @@ -96850,13 +110554,13 @@ simdjson_inline simdjson_result simdjson_result -simdjson_inline error_code simdjson_result::get(westmere::ondemand::document_reference &out) & noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(westmere::ondemand::document_reference &out) & noexcept { if (error()) { return error(); } out = first; return SUCCESS; } template <> -simdjson_inline error_code simdjson_result::get(westmere::ondemand::document_reference &out) && noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(westmere::ondemand::document_reference &out) && noexcept { if (error()) { return error(); } out = first; return SUCCESS; @@ -96944,7 +110648,14 @@ simdjson_inline simdjson_result simdjson_result + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code simdjson_result::extract_into(T& out) & noexcept { + if (error()) { return error(); } + return first.extract_into(out); +} +#endif // SIMDJSON_STATIC_REFLECTION } // namespace simdjson #endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H @@ -97135,10 +110846,19 @@ simdjson_inline document_stream::iterator& document_stream::iterator::operator++ return *this; } +simdjson_inline bool document_stream::iterator::at_end() const noexcept { + return finished; +} + + simdjson_inline bool document_stream::iterator::operator!=(const document_stream::iterator &other) const noexcept { return finished != other.finished; } +simdjson_inline bool document_stream::iterator::operator==(const document_stream::iterator &other) const noexcept { + return finished == other.finished; +} + simdjson_inline document_stream::iterator document_stream::begin() noexcept { start(); // If there are no documents, we're finished. @@ -97256,7 +110976,10 @@ inline void document_stream::next_document() noexcept { // Always set depth=1 at the start of document doc.iter._depth = 1; // consume comma if comma separated is allowed - if (allow_comma_separated) { doc.iter.consume_character(','); } + if (allow_comma_separated) { + error_code ignored = doc.iter.consume_character(','); + static_cast(ignored); // ignored on purpose + } // Resets the string buffer at the beginning, thus invalidating the strings. doc.iter._string_buf_loc = parser->string_buf.get(); doc.iter._root = doc.iter.position(); @@ -97502,7 +111225,7 @@ simdjson_inline simdjson_result simdjson_result -simdjson_inline error_code simdjson_result::unescaped_key(string_type &receiver, bool allow_replacement) noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::unescaped_key(string_type &receiver, bool allow_replacement) noexcept { if (error()) { return error(); } return first.unescaped_key(receiver, allow_replacement); } @@ -97738,6 +111461,8 @@ simdjson_inline void json_iterator::assert_valid_position(token_position positio #ifndef SIMDJSON_CLANG_VISUAL_STUDIO SIMDJSON_ASSUME( position >= &parser->implementation->structural_indexes[0] ); SIMDJSON_ASSUME( position < &parser->implementation->structural_indexes[parser->implementation->n_structural_indexes] ); +#else + (void)position; // Suppress unused parameter warning #endif } @@ -97862,7 +111587,7 @@ simdjson_inline uint8_t *&json_iterator::string_buf_loc() noexcept { return _string_buf_loc; } -simdjson_inline error_code json_iterator::report_error(error_code _error, const char *message) noexcept { +simdjson_warn_unused simdjson_inline error_code json_iterator::report_error(error_code _error, const char *message) noexcept { SIMDJSON_ASSUME(_error != SUCCESS && _error != UNINITIALIZED && _error != INCORRECT_TYPE && _error != NO_SUCH_FIELD); logger::log_error(*this, message); error = _error; @@ -97906,7 +111631,7 @@ simdjson_inline void json_iterator::reenter_child(token_position position, depth _depth = child_depth; } -simdjson_inline error_code json_iterator::consume_character(char c) noexcept { +simdjson_warn_unused simdjson_inline error_code json_iterator::consume_character(char c) noexcept { if (*peek() == c) { return_current_and_advance(); return SUCCESS; @@ -97929,7 +111654,7 @@ simdjson_inline void json_iterator::set_start_position(depth_t depth, token_posi #endif -simdjson_inline error_code json_iterator::optional_error(error_code _error, const char *message) noexcept { +simdjson_warn_unused simdjson_inline error_code json_iterator::optional_error(error_code _error, const char *message) noexcept { SIMDJSON_ASSUME(_error == INCORRECT_TYPE || _error == NO_SUCH_FIELD); logger::log_error(*this, message); return _error; @@ -98324,6 +112049,10 @@ inline void log_line(const json_iterator &iter, token_position index, depth_t de /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value-inl.h" */ +/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_string_builder.h" // for constevalutil::fixed_string */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #endif */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { @@ -98381,7 +112110,7 @@ simdjson_inline simdjson_result object::start_root(value_iterator &iter) SIMDJSON_TRY( iter.start_root_object().error() ); return object(iter); } -simdjson_inline error_code object::consume() noexcept { +simdjson_warn_unused simdjson_inline error_code object::consume() noexcept { if(iter.is_at_key()) { /** * whenever you are pointing at a key, calling skip_child() is @@ -98510,6 +112239,52 @@ simdjson_inline simdjson_result object::reset() & noexcept { return iter.reset_object(); } +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION + +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code object::extract_into(T& out) & noexcept { + // Helper to check if a field name matches any of the requested fields + auto should_extract = [](std::string_view field_name) constexpr -> bool { + return ((FieldNames.view() == field_name) || ...); + }; + + // Iterate through all members of T using reflection + template for (constexpr auto mem : std::define_static_array( + std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) { + + if constexpr (!std::meta::is_const(mem) && std::meta::is_public(mem)) { + constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem)); + + // Only extract this field if it's in our list of requested fields + if constexpr (should_extract(key)) { + // Try to find and extract the field + if constexpr (concepts::optional_type) { + // For optional fields, it's ok if they're missing + auto field_result = find_field_unordered(key); + if (!field_result.error()) { + auto error = field_result.get(out.[:mem:]); + if (error && error != NO_SUCH_FIELD) { + return error; + } + } else if (field_result.error() != NO_SUCH_FIELD) { + return field_result.error(); + } else { + out.[:mem:].reset(); + } + } else { + // For required fields (in the requested list), fail if missing + SIMDJSON_TRY((*this)[key].get(out.[:mem:])); + } + } + } + }; + + return SUCCESS; +} + +#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION + } // namespace ondemand } // namespace westmere } // namespace simdjson @@ -98586,6 +112361,7 @@ simdjson_inline simdjson_result simdjson_result parser::iterate(padded_string_view json) & noexcept { - if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; } + if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; } json.remove_utf8_bom(); @@ -98800,7 +112576,7 @@ simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(p #ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON simdjson_warn_unused simdjson_inline simdjson_result parser::iterate_allow_incomplete_json(padded_string_view json) & noexcept { - if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; } + if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; } json.remove_utf8_bom(); @@ -98832,10 +112608,7 @@ simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(s } simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(std::string &json) & noexcept { - if(json.capacity() - json.size() < SIMDJSON_PADDING) { - json.reserve(json.size() + SIMDJSON_PADDING); - } - return iterate(padded_string_view(json)); + return iterate(pad_with_reserve(json)); } simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(const std::string &json) & noexcept { @@ -98857,7 +112630,7 @@ simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(c } simdjson_warn_unused simdjson_inline simdjson_result parser::iterate_raw(padded_string_view json) & noexcept { - if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; } + if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; } json.remove_utf8_bom(); @@ -98872,6 +112645,7 @@ simdjson_warn_unused simdjson_inline simdjson_result parser::iter } inline simdjson_result parser::iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept { + // Warning: no check is done on the buffer padding. We trust the user. if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; } if((len >= 3) && (std::memcmp(buf, "\xEF\xBB\xBF", 3) == 0)) { buf += 3; @@ -98880,16 +112654,24 @@ inline simdjson_result parser::iterate_many(const uint8_t *buf, if(allow_comma_separated && batch_size < len) { batch_size = len; } return document_stream(*this, buf, len, batch_size, allow_comma_separated); } + inline simdjson_result parser::iterate_many(const char *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept { + // Warning: no check is done on the buffer padding. We trust the user. return iterate_many(reinterpret_cast(buf), len, batch_size, allow_comma_separated); } -inline simdjson_result parser::iterate_many(const std::string &s, size_t batch_size, bool allow_comma_separated) noexcept { +inline simdjson_result parser::iterate_many(padded_string_view s, size_t batch_size, bool allow_comma_separated) noexcept { + if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; } return iterate_many(s.data(), s.length(), batch_size, allow_comma_separated); } inline simdjson_result parser::iterate_many(const padded_string &s, size_t batch_size, bool allow_comma_separated) noexcept { - return iterate_many(s.data(), s.length(), batch_size, allow_comma_separated); + return iterate_many(padded_string_view(s), batch_size, allow_comma_separated); +} +inline simdjson_result parser::iterate_many(const std::string &s, size_t batch_size, bool allow_comma_separated) noexcept { + return iterate_many(padded_string_view(s), batch_size, allow_comma_separated); +} +inline simdjson_result parser::iterate_many(std::string &s, size_t batch_size, bool allow_comma_separated) noexcept { + return iterate_many(pad(s), batch_size, allow_comma_separated); } - simdjson_pure simdjson_inline size_t parser::capacity() const noexcept { return _capacity; } @@ -98924,6 +112706,34 @@ simdjson_inline simdjson_warn_unused simdjson_result parser::u return result; } +simdjson_inline simdjson_warn_unused ondemand::parser& parser::get_parser() { + return *parser::get_parser_instance(); +} + +simdjson_inline bool release_parser() { + auto &parser_instance = parser::get_threadlocal_parser_if_exists(); + if (parser_instance) { + parser_instance.reset(); + return true; + } + return false; +} + +simdjson_inline simdjson_warn_unused std::unique_ptr& parser::get_parser_instance() { + std::unique_ptr& parser_instance = get_threadlocal_parser_if_exists(); + if (!parser_instance) { + parser_instance.reset(new ondemand::parser()); + } + return parser_instance; +} + +simdjson_inline simdjson_warn_unused std::unique_ptr& parser::get_threadlocal_parser_if_exists() { + // @the-moisrex points out that this could be implemented with std::optional (C++17). + thread_local std::unique_ptr parser_instance = nullptr; + return parser_instance; +} + + } // namespace ondemand } // namespace westmere } // namespace simdjson @@ -98958,8 +112768,13 @@ namespace ondemand { simdjson_inline raw_json_string::raw_json_string(const uint8_t * _buf) noexcept : buf{_buf} {} -simdjson_inline const char * raw_json_string::raw() const noexcept { return reinterpret_cast(buf); } +simdjson_inline const char * raw_json_string::raw() const noexcept { + return reinterpret_cast(buf); +} +simdjson_inline char raw_json_string::operator[](size_t i) const noexcept { + return reinterpret_cast(buf)[i]; +} simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(std::string_view target) noexcept { size_t pos{0}; @@ -99136,6 +112951,10 @@ simdjson_inline simdjson_result simdjson_result::operator[](size_t i) const noexcept { + if (error()) { return error(); } + return first[i]; +} simdjson_inline simdjson_warn_unused simdjson_result simdjson_result::unescape(westmere::ondemand::json_iterator &iter, bool allow_replacement) const noexcept { if (error()) { return error(); } return first.unescape(iter, allow_replacement); @@ -99161,6 +112980,9 @@ simdjson_inline simdjson_warn_unused simdjson_result simdjson_ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/serialization.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */ +/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_builder.h" */ +/* amalgamation skipped (editor-only): #endif */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { @@ -99526,7 +113348,7 @@ simdjson_warn_unused simdjson_inline simdjson_result value_iterator::start if (*_json_iter->peek() == '}') { logger::log_value(*_json_iter, "empty object"); _json_iter->return_current_and_advance(); - end_container(); + SIMDJSON_TRY(end_container()); return false; } return true; @@ -100380,7 +114202,7 @@ simdjson_inline void value_iterator::advance_scalar(const char *type) noexcept { _json_iter->ascend_to(depth()-1); } -simdjson_inline error_code value_iterator::start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept { +simdjson_warn_unused simdjson_inline error_code value_iterator::start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept { logger::log_start_value(*_json_iter, start_position(), depth(), type); // If we're not at the position anymore, we don't want to advance the cursor. const uint8_t *json; @@ -100542,7 +114364,7 @@ simdjson_inline simdjson_result value_iterator::type() const noexcept case '5': case '6': case '7': case '8': case '9': return json_type::number; default: - return TAPE_ERROR; + return json_type::unknown; } } @@ -100582,6 +114404,1097 @@ simdjson_inline simdjson_result::simdjson_re #endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H /* end file simdjson/generic/ondemand/value_iterator-inl.h for westmere */ +// JSON builder inline definitions +/* including simdjson/generic/ondemand/json_string_builder-inl.h for westmere: #include "simdjson/generic/ondemand/json_string_builder-inl.h" */ +/* begin file simdjson/generic/ondemand/json_string_builder-inl.h for westmere */ +/** + * This file is part of the builder API. It is temporarily in the ondemand + * directory but we will move it to a builder directory later. + */ +#include +#include +#include +#ifndef SIMDJSON_GENERIC_STRING_BUILDER_INL_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_INL_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_string_builder.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +/* + * Empirically, we have found that an inlined optimization is important for + * performance. The following macros are not ideal. We should find a better + * way to inline the code. + */ + +#if defined(__SSE2__) || defined(__x86_64__) || defined(__x86_64) || \ + (defined(_M_AMD64) || defined(_M_X64) || \ + (defined(_M_IX86_FP) && _M_IX86_FP == 2)) +#ifndef SIMDJSON_EXPERIMENTAL_HAS_SSE2 +#define SIMDJSON_EXPERIMENTAL_HAS_SSE2 1 +#endif +#endif + +#if defined(__aarch64__) || defined(_M_ARM64) +#ifndef SIMDJSON_EXPERIMENTAL_HAS_NEON +#define SIMDJSON_EXPERIMENTAL_HAS_NEON 1 +#endif +#endif +#if SIMDJSON_EXPERIMENTAL_HAS_NEON +#include +#endif +#if SIMDJSON_EXPERIMENTAL_HAS_SSE2 +#include +#endif + +namespace simdjson { +namespace westmere { +namespace builder { + +static SIMDJSON_CONSTEXPR_LAMBDA std::array + json_quotable_character = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 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, 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, 1, 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, 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, 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, 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, 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, 0, 0, 0}; + +/** + +A possible SWAR implementation of has_json_escapable_byte. It is not used +because it is slower than the current implementation. It is kept here for +reference (to show that we tried it). + +inline bool has_json_escapable_byte(uint64_t x) { + uint64_t is_ascii = 0x8080808080808080ULL & ~x; + uint64_t xor2 = x ^ 0x0202020202020202ULL; + uint64_t lt32_or_eq34 = xor2 - 0x2121212121212121ULL; + uint64_t sub92 = x ^ 0x5C5C5C5C5C5C5C5CULL; + uint64_t eq92 = (sub92 - 0x0101010101010101ULL); + return ((lt32_or_eq34 | eq92) & is_ascii) != 0; +} + +**/ + +SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline bool +simple_needs_escaping(std::string_view v) { + for (char c : v) { + // a table lookup is faster than a series of comparisons + if (json_quotable_character[static_cast(c)]) { + return true; + } + } + return false; +} + +#if SIMDJSON_EXPERIMENTAL_HAS_NEON +simdjson_inline bool fast_needs_escaping(std::string_view view) { + if (view.size() < 16) { + return simple_needs_escaping(view); + } + size_t i = 0; + uint8x16_t running = vdupq_n_u8(0); + uint8x16_t v34 = vdupq_n_u8(34); + uint8x16_t v92 = vdupq_n_u8(92); + + for (; i + 15 < view.size(); i += 16) { + uint8x16_t word = vld1q_u8((const uint8_t *)view.data() + i); + running = vorrq_u8(running, vceqq_u8(word, v34)); + running = vorrq_u8(running, vceqq_u8(word, v92)); + running = vorrq_u8(running, vcltq_u8(word, vdupq_n_u8(32))); + } + if (i < view.size()) { + uint8x16_t word = + vld1q_u8((const uint8_t *)view.data() + view.length() - 16); + running = vorrq_u8(running, vceqq_u8(word, v34)); + running = vorrq_u8(running, vceqq_u8(word, v92)); + running = vorrq_u8(running, vcltq_u8(word, vdupq_n_u8(32))); + } + return vmaxvq_u32(vreinterpretq_u32_u8(running)) != 0; +} +#elif SIMDJSON_EXPERIMENTAL_HAS_SSE2 +simdjson_inline bool fast_needs_escaping(std::string_view view) { + if (view.size() < 16) { + return simple_needs_escaping(view); + } + size_t i = 0; + __m128i running = _mm_setzero_si128(); + for (; i + 15 < view.size(); i += 16) { + + __m128i word = + _mm_loadu_si128(reinterpret_cast(view.data() + i)); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(34))); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(92))); + running = _mm_or_si128( + running, _mm_cmpeq_epi8(_mm_subs_epu8(word, _mm_set1_epi8(31)), + _mm_setzero_si128())); + } + if (i < view.size()) { + __m128i word = _mm_loadu_si128( + reinterpret_cast(view.data() + view.length() - 16)); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(34))); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(92))); + running = _mm_or_si128( + running, _mm_cmpeq_epi8(_mm_subs_epu8(word, _mm_set1_epi8(31)), + _mm_setzero_si128())); + } + return _mm_movemask_epi8(running) != 0; +} +#else +simdjson_inline bool fast_needs_escaping(std::string_view view) { + return simple_needs_escaping(view); +} +#endif + +SIMDJSON_CONSTEXPR_LAMBDA inline size_t +find_next_json_quotable_character(const std::string_view view, + size_t location) noexcept { + + for (auto pos = view.begin() + location; pos != view.end(); ++pos) { + if (json_quotable_character[static_cast(*pos)]) { + return pos - view.begin(); + } + } + return size_t(view.size()); +} + +SIMDJSON_CONSTEXPR_LAMBDA static std::string_view control_chars[] = { + "\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", "\\u0006", + "\\u0007", "\\b", "\\t", "\\n", "\\u000b", "\\f", "\\r", + "\\u000e", "\\u000f", "\\u0010", "\\u0011", "\\u0012", "\\u0013", "\\u0014", + "\\u0015", "\\u0016", "\\u0017", "\\u0018", "\\u0019", "\\u001a", "\\u001b", + "\\u001c", "\\u001d", "\\u001e", "\\u001f"}; + +// All Unicode characters may be placed within the quotation marks, except for +// the characters that MUST be escaped: quotation mark, reverse solidus, and the +// control characters (U+0000 through U+001F). There are two-character sequence +// escape representations of some popular characters: +// \", \\, \b, \f, \n, \r, \t. +SIMDJSON_CONSTEXPR_LAMBDA void escape_json_char(char c, char *&out) { + if (c == '"') { + memcpy(out, "\\\"", 2); + out += 2; + } else if (c == '\\') { + memcpy(out, "\\\\", 2); + out += 2; + } else { + std::string_view v = control_chars[uint8_t(c)]; + memcpy(out, v.data(), v.size()); + out += v.size(); + } +} + +inline size_t write_string_escaped(const std::string_view input, char *out) { + size_t mysize = input.size(); + if (!fast_needs_escaping(input)) { // fast path! + memcpy(out, input.data(), input.size()); + return input.size(); + } + const char *const initout = out; + size_t location = find_next_json_quotable_character(input, 0); + memcpy(out, input.data(), location); + out += location; + escape_json_char(input[location], out); + location += 1; + while (location < mysize) { + size_t newlocation = find_next_json_quotable_character(input, location); + memcpy(out, input.data() + location, newlocation - location); + out += newlocation - location; + location = newlocation; + if (location == mysize) { + break; + } + escape_json_char(input[location], out); + location += 1; + } + return out - initout; +} + +simdjson_inline string_builder::string_builder(size_t initial_capacity) + : buffer(new(std::nothrow) char[initial_capacity]), position(0), + capacity(buffer.get() != nullptr ? initial_capacity : 0), + is_valid(buffer.get() != nullptr) {} + +simdjson_inline bool string_builder::capacity_check(size_t upcoming_bytes) { + // We use the convention that when is_valid is false, then the capacity and + // the position are 0. + // Most of the time, this function will return true. + if (simdjson_likely(upcoming_bytes <= capacity - position)) { + return true; + } + // check for overflow, most of the time there is no overflow + if (simdjson_likely(position + upcoming_bytes < position)) { + return false; + } + // We will rarely get here. + grow_buffer((std::max)(capacity * 2, position + upcoming_bytes)); + // If the buffer allocation failed, we set is_valid to false. + return is_valid; +} + +simdjson_inline void string_builder::grow_buffer(size_t desired_capacity) { + if (!is_valid) { + return; + } + std::unique_ptr new_buffer(new (std::nothrow) char[desired_capacity]); + if (new_buffer.get() == nullptr) { + set_valid(false); + return; + } + std::memcpy(new_buffer.get(), buffer.get(), position); + buffer.swap(new_buffer); + capacity = desired_capacity; +} + +simdjson_inline void string_builder::set_valid(bool valid) noexcept { + if (!valid) { + is_valid = false; + capacity = 0; + position = 0; + buffer.reset(); + } else { + is_valid = true; + } +} + +simdjson_inline size_t string_builder::size() const noexcept { + return position; +} + +simdjson_inline void string_builder::append(char c) noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = c; + } +} + +simdjson_inline void string_builder::append_null() noexcept { + constexpr char null_literal[] = "null"; + constexpr size_t null_len = sizeof(null_literal) - 1; + if (capacity_check(null_len)) { + std::memcpy(buffer.get() + position, null_literal, null_len); + position += null_len; + } +} + +simdjson_inline void string_builder::clear() noexcept { + position = 0; + // if it was invalid, we should try to repair it + if (!is_valid) { + capacity = 0; + buffer.reset(); + is_valid = true; + } +} + +namespace internal { + +template ::value>::type> +simdjson_really_inline int int_log2(number_type x) { + return 63 - leading_zeroes(uint64_t(x) | 1); +} + +simdjson_really_inline int fast_digit_count_32(uint32_t x) { + static uint64_t table[] = { + 4294967296, 8589934582, 8589934582, 8589934582, 12884901788, + 12884901788, 12884901788, 17179868184, 17179868184, 17179868184, + 21474826480, 21474826480, 21474826480, 21474826480, 25769703776, + 25769703776, 25769703776, 30063771072, 30063771072, 30063771072, + 34349738368, 34349738368, 34349738368, 34349738368, 38554705664, + 38554705664, 38554705664, 41949672960, 41949672960, 41949672960, + 42949672960, 42949672960}; + return uint32_t((x + table[int_log2(x)]) >> 32); +} + +simdjson_really_inline int fast_digit_count_64(uint64_t x) { + static uint64_t table[] = {9, + 99, + 999, + 9999, + 99999, + 999999, + 9999999, + 99999999, + 999999999, + 9999999999, + 99999999999, + 999999999999, + 9999999999999, + 99999999999999, + 999999999999999ULL, + 9999999999999999ULL, + 99999999999999999ULL, + 999999999999999999ULL, + 9999999999999999999ULL}; + int y = (19 * int_log2(x) >> 6); + y += x > table[y]; + return y + 1; +} + +template ::value>::type> +simdjson_really_inline size_t digit_count(number_type v) noexcept { + static_assert(sizeof(number_type) == 8 || sizeof(number_type) == 4 || + sizeof(number_type) == 2 || sizeof(number_type) == 1, + "We only support 8-bit, 16-bit, 32-bit and 64-bit numbers"); + SIMDJSON_IF_CONSTEXPR(sizeof(number_type) <= 4) { + return fast_digit_count_32(static_cast(v)); + } + else { + return fast_digit_count_64(static_cast(v)); + } +} +static const char decimal_table[200] = { + 0x30, 0x30, 0x30, 0x31, 0x30, 0x32, 0x30, 0x33, 0x30, 0x34, 0x30, 0x35, + 0x30, 0x36, 0x30, 0x37, 0x30, 0x38, 0x30, 0x39, 0x31, 0x30, 0x31, 0x31, + 0x31, 0x32, 0x31, 0x33, 0x31, 0x34, 0x31, 0x35, 0x31, 0x36, 0x31, 0x37, + 0x31, 0x38, 0x31, 0x39, 0x32, 0x30, 0x32, 0x31, 0x32, 0x32, 0x32, 0x33, + 0x32, 0x34, 0x32, 0x35, 0x32, 0x36, 0x32, 0x37, 0x32, 0x38, 0x32, 0x39, + 0x33, 0x30, 0x33, 0x31, 0x33, 0x32, 0x33, 0x33, 0x33, 0x34, 0x33, 0x35, + 0x33, 0x36, 0x33, 0x37, 0x33, 0x38, 0x33, 0x39, 0x34, 0x30, 0x34, 0x31, + 0x34, 0x32, 0x34, 0x33, 0x34, 0x34, 0x34, 0x35, 0x34, 0x36, 0x34, 0x37, + 0x34, 0x38, 0x34, 0x39, 0x35, 0x30, 0x35, 0x31, 0x35, 0x32, 0x35, 0x33, + 0x35, 0x34, 0x35, 0x35, 0x35, 0x36, 0x35, 0x37, 0x35, 0x38, 0x35, 0x39, + 0x36, 0x30, 0x36, 0x31, 0x36, 0x32, 0x36, 0x33, 0x36, 0x34, 0x36, 0x35, + 0x36, 0x36, 0x36, 0x37, 0x36, 0x38, 0x36, 0x39, 0x37, 0x30, 0x37, 0x31, + 0x37, 0x32, 0x37, 0x33, 0x37, 0x34, 0x37, 0x35, 0x37, 0x36, 0x37, 0x37, + 0x37, 0x38, 0x37, 0x39, 0x38, 0x30, 0x38, 0x31, 0x38, 0x32, 0x38, 0x33, + 0x38, 0x34, 0x38, 0x35, 0x38, 0x36, 0x38, 0x37, 0x38, 0x38, 0x38, 0x39, + 0x39, 0x30, 0x39, 0x31, 0x39, 0x32, 0x39, 0x33, 0x39, 0x34, 0x39, 0x35, + 0x39, 0x36, 0x39, 0x37, 0x39, 0x38, 0x39, 0x39, +}; +} // namespace internal + +template +simdjson_inline void string_builder::append(number_type v) noexcept { + static_assert(std::is_same::value || + std::is_integral::value || + std::is_floating_point::value, + "Unsupported number type"); + // If C++17 is available, we can 'if constexpr' here. + SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + if (v) { + constexpr char true_literal[] = "true"; + constexpr size_t true_len = sizeof(true_literal) - 1; + if (capacity_check(true_len)) { + std::memcpy(buffer.get() + position, true_literal, true_len); + position += true_len; + } + } else { + constexpr char false_literal[] = "false"; + constexpr size_t false_len = sizeof(false_literal) - 1; + if (capacity_check(false_len)) { + std::memcpy(buffer.get() + position, false_literal, false_len); + position += false_len; + } + } + } + else SIMDJSON_IF_CONSTEXPR(std::is_unsigned::value) { + constexpr size_t max_number_size = 20; + if (capacity_check(max_number_size)) { + using unsigned_type = typename std::make_unsigned::type; + unsigned_type pv = static_cast(v); + size_t dc = internal::digit_count(pv); + char *write_pointer = buffer.get() + position + dc - 1; + while (pv >= 100) { + memcpy(write_pointer - 1, &internal::decimal_table[(pv % 100) * 2], 2); + write_pointer -= 2; + pv /= 100; + } + if (pv >= 10) { + *write_pointer-- = char('0' + (pv % 10)); + pv /= 10; + } + *write_pointer = char('0' + pv); + position += dc; + } + } + else SIMDJSON_IF_CONSTEXPR(std::is_integral::value) { + constexpr size_t max_number_size = 20; + if (capacity_check(max_number_size)) { + using unsigned_type = typename std::make_unsigned::type; + bool negative = v < 0; + unsigned_type pv = static_cast(v); + if (negative) { + pv = 0 - pv; // the 0 is for Microsoft + } + size_t dc = internal::digit_count(pv); + if (negative) { + buffer.get()[position++] = '-'; + } + char *write_pointer = buffer.get() + position + dc - 1; + while (pv >= 100) { + memcpy(write_pointer - 1, &internal::decimal_table[(pv % 100) * 2], 2); + write_pointer -= 2; + pv /= 100; + } + if (pv >= 10) { + *write_pointer-- = char('0' + (pv % 10)); + pv /= 10; + } + *write_pointer = char('0' + pv); + position += dc; + } + } + else SIMDJSON_IF_CONSTEXPR(std::is_floating_point::value) { + constexpr size_t max_number_size = 24; + if (capacity_check(max_number_size)) { + // We could specialize for float. + char *end = simdjson::internal::to_chars(buffer.get() + position, nullptr, + double(v)); + position = end - buffer.get(); + } + } +} + +simdjson_inline void +string_builder::escape_and_append(std::string_view input) noexcept { + // escaping might turn a control character into \x00xx so 6 characters. + if (capacity_check(6 * input.size())) { + position += write_string_escaped(input, buffer.get() + position); + } +} + +simdjson_inline void +string_builder::escape_and_append_with_quotes(std::string_view input) noexcept { + // escaping might turn a control character into \x00xx so 6 characters. + if (capacity_check(2 + 6 * input.size())) { + buffer.get()[position++] = '"'; + position += write_string_escaped(input, buffer.get() + position); + buffer.get()[position++] = '"'; + } +} + +simdjson_inline void +string_builder::escape_and_append_with_quotes(char input) noexcept { + // escaping might turn a control character into \x00xx so 6 characters. + if (capacity_check(2 + 6 * 1)) { + buffer.get()[position++] = '"'; + std::string_view cinput(&input, 1); + position += write_string_escaped(cinput, buffer.get() + position); + buffer.get()[position++] = '"'; + } +} + +simdjson_inline void +string_builder::escape_and_append_with_quotes(const char *input) noexcept { + std::string_view cinput(input); + escape_and_append_with_quotes(cinput); +} +#if SIMDJSON_SUPPORTS_CONCEPTS +template +simdjson_inline void string_builder::escape_and_append_with_quotes() noexcept { + escape_and_append_with_quotes(constevalutil::string_constant::value); +} +#endif + +simdjson_inline void string_builder::append_raw(const char *c) noexcept { + size_t len = std::strlen(c); + append_raw(c, len); +} + +simdjson_inline void +string_builder::append_raw(std::string_view input) noexcept { + if (capacity_check(input.size())) { + std::memcpy(buffer.get() + position, input.data(), input.size()); + position += input.size(); + } +} + +simdjson_inline void string_builder::append_raw(const char *str, + size_t len) noexcept { + if (capacity_check(len)) { + std::memcpy(buffer.get() + position, str, len); + position += len; + } +} +#if SIMDJSON_SUPPORTS_CONCEPTS +// Support for optional types (std::optional, etc.) +template + requires(!require_custom_serialization) +simdjson_inline void string_builder::append(const T &opt) { + if (opt) { + append(*opt); + } else { + append_null(); + } +} + +template + requires(require_custom_serialization) +simdjson_inline void string_builder::append(const T &val) { + serialize(*this, val); +} + +template + requires(std::is_convertible::value || + std::is_same::value) +simdjson_inline void string_builder::append(const T &value) { + escape_and_append_with_quotes(value); +} +#endif + +#if SIMDJSON_SUPPORTS_RANGES && SIMDJSON_SUPPORTS_CONCEPTS +// Support for range-based appending (std::ranges::view, etc.) +template + requires(!std::is_convertible::value) +simdjson_inline void string_builder::append(const R &range) noexcept { + auto it = std::ranges::begin(range); + auto end = std::ranges::end(range); + if constexpr (concepts::is_pair) { + start_object(); + + if (it == end) { + end_object(); + return; // Handle empty range + } + // Append first item without leading comma + append_key_value(it->first, it->second); + ++it; + + // Append remaining items with preceding commas + for (; it != end; ++it) { + append_comma(); + append_key_value(it->first, it->second); + } + end_object(); + } else { + start_array(); + if (it == end) { + end_array(); + return; // Handle empty range + } + + // Append first item without leading comma + append(*it); + ++it; + + // Append remaining items with preceding commas + for (; it != end; ++it) { + append_comma(); + append(*it); + } + end_array(); + } +} + +#endif + +#if SIMDJSON_EXCEPTIONS +simdjson_inline string_builder::operator std::string() const noexcept(false) { + return std::string(operator std::string_view()); +} + +simdjson_inline string_builder::operator std::string_view() const + noexcept(false) simdjson_lifetime_bound { + return view(); +} +#endif + +simdjson_inline simdjson_result +string_builder::view() const noexcept { + if (!is_valid) { + return simdjson::OUT_OF_CAPACITY; + } + return std::string_view(buffer.get(), position); +} + +simdjson_inline simdjson_result string_builder::c_str() noexcept { + if (capacity_check(1)) { + buffer.get()[position] = '\0'; + return buffer.get(); + } + return simdjson::OUT_OF_CAPACITY; +} + +simdjson_inline bool string_builder::validate_unicode() const noexcept { + return simdjson::validate_utf8(buffer.get(), position); +} + +simdjson_inline void string_builder::start_object() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = '{'; + } +} + +simdjson_inline void string_builder::end_object() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = '}'; + } +} + +simdjson_inline void string_builder::start_array() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = '['; + } +} + +simdjson_inline void string_builder::end_array() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = ']'; + } +} + +simdjson_inline void string_builder::append_comma() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = ','; + } +} + +simdjson_inline void string_builder::append_colon() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = ':'; + } +} + +template +simdjson_inline void +string_builder::append_key_value(key_type key, value_type value) noexcept { + static_assert(std::is_same::value || + std::is_convertible::value, + "Unsupported key type"); + escape_and_append_with_quotes(key); + append_colon(); + SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + append_null(); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR( + std::is_convertible::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else { + append(value); + } +} + +#if SIMDJSON_SUPPORTS_CONCEPTS +template +simdjson_inline void +string_builder::append_key_value(value_type value) noexcept { + escape_and_append_with_quotes(); + append_colon(); + SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + append_null(); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR( + std::is_convertible::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else { + append(value); + } +} +#endif + +} // namespace builder +} // namespace westmere +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_STRING_BUILDER_INL_H +/* end file simdjson/generic/ondemand/json_string_builder-inl.h for westmere */ +/* including simdjson/generic/ondemand/json_builder.h for westmere: #include "simdjson/generic/ondemand/json_builder.h" */ +/* begin file simdjson/generic/ondemand/json_builder.h for westmere */ +/** + * This file is part of the builder API. It is temporarily in the ondemand directory + * but we will move it to a builder directory later. + */ +#ifndef SIMDJSON_GENERIC_BUILDER_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_string_builder.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/concepts.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ +#if SIMDJSON_STATIC_REFLECTION + +#include +#include +#include +#include +#include +#include +#include +#include +// #include // for std::define_static_string - header not available yet + +namespace simdjson { +namespace westmere { +namespace builder { + +template + requires(concepts::container_but_not_string && !require_custom_serialization) +constexpr void atom(string_builder &b, const T &t) { + auto it = t.begin(); + auto end = t.end(); + if (it == end) { + b.append_raw("[]"); + return; + } + b.append('['); + atom(b, *it); + ++it; + for (; it != end; ++it) { + b.append(','); + atom(b, *it); + } + b.append(']'); +} + +template + requires(std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v) +constexpr void atom(string_builder &b, const T &t) { + b.escape_and_append_with_quotes(t); +} + +template + requires(!require_custom_serialization) +constexpr void atom(string_builder &b, const T &m) { + if (m.empty()) { + b.append_raw("{}"); + return; + } + b.append('{'); + bool first = true; + for (const auto& [key, value] : m) { + if (!first) { + b.append(','); + } + first = false; + // Keys must be convertible to string_view per the concept + b.escape_and_append_with_quotes(key); + b.append(':'); + atom(b, value); + } + b.append('}'); +} + + +template::value && !std::is_same_v>::type> +constexpr void atom(string_builder &b, const number_type t) { + b.append(t); +} + +template + requires(std::is_class_v && !concepts::container_but_not_string && + !concepts::string_view_keyed_map && + !concepts::optional_type && + !concepts::smart_pointer && + !concepts::appendable_containers && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && !require_custom_serialization) +constexpr void atom(string_builder &b, const T &t) { + int i = 0; + b.append('{'); + template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) { + if (i != 0) + b.append(','); + constexpr auto key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(dm))); + b.append_raw(key); + b.append(':'); + atom(b, t.[:dm:]); + i++; + }; + b.append('}'); +} + +// Support for optional types (std::optional, etc.) +template + requires(!require_custom_serialization) +constexpr void atom(string_builder &b, const T &opt) { + if (opt) { + atom(b, opt.value()); + } else { + b.append_raw("null"); + } +} + +// Support for smart pointers (std::unique_ptr, std::shared_ptr, etc.) +template + requires(!require_custom_serialization) +constexpr void atom(string_builder &b, const T &ptr) { + if (ptr) { + atom(b, *ptr); + } else { + b.append_raw("null"); + } +} + +// Support for enums - serialize as string representation using expand approach from P2996R12 +template + requires(std::is_enum_v && !require_custom_serialization) +void atom(string_builder &b, const T &e) { +#if SIMDJSON_STATIC_REFLECTION + constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T)); + template for (constexpr auto enum_val : enumerators) { + constexpr auto enum_str = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(enum_val))); + if (e == [:enum_val:]) { + b.append_raw(enum_str); + return; + } + }; + // Fallback to integer if enum value not found + atom(b, static_cast>(e)); +#else + // Fallback: serialize as integer if reflection not available + atom(b, static_cast>(e)); +#endif +} + +// Support for appendable containers that don't have operator[] (sets, etc.) +template + requires(!concepts::container_but_not_string && !concepts::string_view_keyed_map && + !concepts::optional_type && !concepts::smart_pointer && + !std::is_same_v && + !std::is_same_v && !std::is_same_v && !require_custom_serialization) +constexpr void atom(string_builder &b, const T &container) { + if (container.empty()) { + b.append_raw("[]"); + return; + } + b.append('['); + bool first = true; + for (const auto& item : container) { + if (!first) { + b.append(','); + } + first = false; + atom(b, item); + } + b.append(']'); +} + +// append functions that delegate to atom functions for primitive types +template + requires(std::is_arithmetic_v && !std::is_same_v) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!concepts::container_but_not_string && !concepts::string_view_keyed_map && + !concepts::optional_type && !concepts::smart_pointer && + !std::is_same_v && + !std::is_same_v && !std::is_same_v && !require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +// works for struct +template + requires(std::is_class_v && !concepts::container_but_not_string && + !concepts::string_view_keyed_map && + !concepts::optional_type && + !concepts::smart_pointer && + !concepts::appendable_containers && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && !require_custom_serialization) +void append(string_builder &b, const Z &z) { + int i = 0; + b.append('{'); + template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^Z, std::meta::access_context::unchecked()))) { + if (i != 0) + b.append(','); + constexpr auto key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(dm))); + b.append_raw(key); + b.append(':'); + atom(b, z.[:dm:]); + i++; + }; + b.append('}'); +} + +// works for container that have begin() and end() iterators +template + requires(concepts::container_but_not_string && !require_custom_serialization) +void append(string_builder &b, const Z &z) { + auto it = z.begin(); + auto end = z.end(); + if (it == end) { + b.append_raw("[]"); + return; + } + b.append('['); + atom(b, *it); + ++it; + for (; it != end; ++it) { + b.append(','); + atom(b, *it); + } + b.append(']'); +} + +template + requires (require_custom_serialization) +void append(string_builder &b, const Z &z) { + b.append(z); +} + + +template +simdjson_warn_unused simdjson_result to_json_string(const Z &z, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) { + string_builder b(initial_capacity); + append(b, z); + std::string_view s; + if(auto e = b.view().get(s); e) { return e; } + return std::string(s); +} + +template +simdjson_warn_unused simdjson_error to_json(const Z &z, std::string &s, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) { + string_builder b(initial_capacity); + append(b, z); + std::string_view view; + if(auto e = b.view().get(view); e) { return e; } + s.assign(view); + return SUCCESS; +} + +template +string_builder& operator<<(string_builder& b, const Z& z) { + append(b, z); + return b; +} + +// extract_from: Serialize only specific fields from a struct to JSON +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +void extract_from(string_builder &b, const T &obj) { + // Helper to check if a field name matches any of the requested fields + auto should_extract = [](std::string_view field_name) constexpr -> bool { + return ((FieldNames.view() == field_name) || ...); + }; + + b.append('{'); + bool first = true; + + // Iterate through all members of T using reflection + template for (constexpr auto mem : std::define_static_array( + std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) { + + if constexpr (std::meta::is_public(mem)) { + constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem)); + + // Only serialize this field if it's in our list of requested fields + if constexpr (should_extract(key)) { + if (!first) { + b.append(','); + } + first = false; + + // Serialize the key + constexpr auto quoted_key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(mem))); + b.append_raw(quoted_key); + b.append(':'); + + // Serialize the value + atom(b, obj.[:mem:]); + } + } + }; + + b.append('}'); +} + +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_result extract_from(const T &obj, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) { + string_builder b(initial_capacity); + extract_from(b, obj); + std::string_view s; + if(auto e = b.view().get(s); e) { return e; } + return std::string(s); +} + +} // namespace builder +} // namespace westmere +// Alias the function template to 'to' in the global namespace +template +simdjson_warn_unused simdjson_result to_json(const Z &z, size_t initial_capacity = westmere::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { + westmere::builder::string_builder b(initial_capacity); + westmere::builder::append(b, z); + std::string_view s; + if(auto e = b.view().get(s); e) { return e; } + return std::string(s); +} +template +simdjson_warn_unused simdjson_error to_json(const Z &z, std::string &s, size_t initial_capacity = westmere::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { + westmere::builder::string_builder b(initial_capacity); + westmere::builder::append(b, z); + std::string_view view; + if(auto e = b.view().get(view); e) { return e; } + s.assign(view); + return SUCCESS; +} +// Global namespace function for extract_from +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_result extract_from(const T &obj, size_t initial_capacity = westmere::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { + westmere::builder::string_builder b(initial_capacity); + westmere::builder::extract_from(b, obj); + std::string_view s; + if(auto e = b.view().get(s); e) { return e; } + return std::string(s); +} + +} // namespace simdjson + +#endif // SIMDJSON_STATIC_REFLECTION + +#endif +/* end file simdjson/generic/ondemand/json_builder.h for westmere */ /* end file simdjson/generic/ondemand/amalgamated.h for westmere */ /* including simdjson/westmere/end.h: #include "simdjson/westmere/end.h" */ @@ -101171,7 +116084,7 @@ using namespace simd; struct backslash_and_quote { public: static constexpr uint32_t BYTES_PROCESSED = 32; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); + simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; } simdjson_inline bool has_backslash() { return bs_bits != 0; } @@ -101200,6 +116113,31 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin }; } + +struct escaping { + static constexpr uint32_t BYTES_PROCESSED = 16; + simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst); + + simdjson_inline bool has_escape() { return escape_bits != 0; } + simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); } + + uint64_t escape_bits; +}; // struct escaping + + + +simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) { + static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes"); + simd8 v(src); + v.store(dst); + simd8 is_quote = (v == '"'); + simd8 is_backslash = (v == '\\'); + simd8 is_control = (v < 32); + return { + static_cast((is_backslash | is_quote | is_control).to_bitmask()) + }; +} + } // unnamed namespace } // namespace lsx } // namespace simdjson @@ -101268,7 +116206,7 @@ class value_iterator; /* end file simdjson/generic/ondemand/base.h for lsx */ /* including simdjson/generic/ondemand/deserialize.h for lsx: #include "simdjson/generic/ondemand/deserialize.h" */ /* begin file simdjson/generic/ondemand/deserialize.h for lsx */ -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS #ifndef SIMDJSON_ONDEMAND_DESERIALIZE_H /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ @@ -101277,55 +116215,8 @@ class value_iterator; /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ -#include namespace simdjson { -namespace tag_invoke_fn_ns { -void tag_invoke(); - -struct tag_invoke_fn { - template - requires requires(Tag tag, Args &&...args) { - tag_invoke(std::forward(tag), std::forward(args)...); - } - constexpr auto operator()(Tag tag, Args &&...args) const - noexcept(noexcept(tag_invoke(std::forward(tag), - std::forward(args)...))) - -> decltype(tag_invoke(std::forward(tag), - std::forward(args)...)) { - return tag_invoke(std::forward(tag), std::forward(args)...); - } -}; -} // namespace tag_invoke_fn_ns - -inline namespace tag_invoke_ns { -inline constexpr tag_invoke_fn_ns::tag_invoke_fn tag_invoke = {}; -} // namespace tag_invoke_ns - -template -concept tag_invocable = requires(Tag tag, Args... args) { - tag_invoke(std::forward(tag), std::forward(args)...); -}; - -template -concept nothrow_tag_invocable = - tag_invocable && requires(Tag tag, Args... args) { - { - tag_invoke(std::forward(tag), std::forward(args)...) - } noexcept; - }; - -template -using tag_invoke_result = - std::invoke_result; - -template -using tag_invoke_result_t = - std::invoke_result_t; - -template using tag_t = std::decay_t; - - struct deserialize_tag; /// These types are deserializable in a built-in way @@ -101347,7 +116238,7 @@ template concept custom_deserializable = tag_invocable; template -concept deserializable = custom_deserializable || is_builtin_deserializable_v; +concept deserializable = custom_deserializable || is_builtin_deserializable_v || concepts::optional_type; template concept nothrow_custom_deserializable = nothrow_tag_invocable; @@ -101358,28 +116249,44 @@ concept nothrow_deserializable = nothrow_custom_deserializable || is_bu /// Deserialize Tag inline constexpr struct deserialize_tag { + using array_type = lsx::ondemand::array; + using object_type = lsx::ondemand::object; using value_type = lsx::ondemand::value; using document_type = lsx::ondemand::document; using document_reference_type = lsx::ondemand::document_reference; + // Customization Point for array + template + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(array_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for object + template + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(object_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + // Customization Point for value template requires custom_deserializable - [[nodiscard]] constexpr /* error_code */ auto operator()(value_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + simdjson_warn_unused constexpr /* error_code */ auto operator()(value_type &object, T& output) const noexcept(nothrow_custom_deserializable) { return tag_invoke(*this, object, output); } // Customization Point for document template requires custom_deserializable - [[nodiscard]] constexpr /* error_code */ auto operator()(document_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + simdjson_warn_unused constexpr /* error_code */ auto operator()(document_type &object, T& output) const noexcept(nothrow_custom_deserializable) { return tag_invoke(*this, object, output); } // Customization Point for document reference template requires custom_deserializable - [[nodiscard]] constexpr /* error_code */ auto operator()(document_reference_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + simdjson_warn_unused constexpr /* error_code */ auto operator()(document_reference_type &object, T& output) const noexcept(nothrow_custom_deserializable) { return tag_invoke(*this, object, output); } @@ -101389,7 +116296,7 @@ inline constexpr struct deserialize_tag { } // namespace simdjson #endif // SIMDJSON_ONDEMAND_DESERIALIZE_H -#endif // SIMDJSON_SUPPORTS_DESERIALIZATION +#endif // SIMDJSON_SUPPORTS_CONCEPTS /* end file simdjson/generic/ondemand/deserialize.h for lsx */ /* including simdjson/generic/ondemand/value_iterator.h for lsx: #include "simdjson/generic/ondemand/value_iterator.h" */ @@ -101731,7 +116638,7 @@ class value_iterator { simdjson_warn_unused simdjson_inline simdjson_result get_root_number(bool check_trailing) noexcept; simdjson_warn_unused simdjson_inline simdjson_result is_root_null(bool check_trailing) noexcept; - simdjson_inline error_code error() const noexcept; + simdjson_warn_unused simdjson_inline error_code error() const noexcept; simdjson_inline uint8_t *&string_buf_loc() noexcept; simdjson_inline const json_iterator &json_iter() const noexcept; simdjson_inline json_iterator &json_iter() noexcept; @@ -101815,8 +116722,8 @@ class value_iterator { simdjson_inline const uint8_t *peek_non_root_scalar(const char *type) noexcept; - simdjson_inline error_code start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept; - simdjson_inline error_code end_container() noexcept; + simdjson_warn_unused simdjson_inline error_code start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept; + simdjson_warn_unused simdjson_inline error_code end_container() noexcept; /** * Advance to a place expecting a value (increasing depth). @@ -101826,8 +116733,8 @@ class value_iterator { */ simdjson_inline simdjson_result advance_to_value() noexcept; - simdjson_inline error_code incorrect_type_error(const char *message) const noexcept; - simdjson_inline error_code error_unless_more_tokens(uint32_t tokens=1) const noexcept; + simdjson_warn_unused simdjson_inline error_code incorrect_type_error(const char *message) const noexcept; + simdjson_warn_unused simdjson_inline error_code error_unless_more_tokens(uint32_t tokens=1) const noexcept; simdjson_inline bool is_at_start() const noexcept; /** @@ -101864,7 +116771,7 @@ class value_iterator { /** @copydoc error_code json_iterator::end_position() const noexcept; */ simdjson_inline token_position end_position() const noexcept; /** @copydoc error_code json_iterator::report_error(error_code error, const char *message) noexcept; */ - simdjson_inline error_code report_error(error_code error, const char *message) noexcept; + simdjson_warn_unused simdjson_inline error_code report_error(error_code error, const char *message) noexcept; friend class document; friend class object; @@ -101929,13 +116836,14 @@ class value { * * You may use get_double(), get_bool(), get_uint64(), get_int64(), * get_object(), get_array(), get_raw_json_string(), or get_string() instead. + * When SIMDJSON_SUPPORTS_CONCEPTS is set, custom types are also supported. * * @returns A value of the given type, parsed from the JSON. * @returns INCORRECT_TYPE If the JSON value is not the given type. */ template simdjson_inline simdjson_result get() -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept @@ -101952,22 +116860,38 @@ class value { * Get this value as the given type. * * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool + * If the macro SIMDJSON_SUPPORTS_CONCEPTS is set, then custom types are also supported. * * @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized. * @returns INCORRECT_TYPE If the JSON value is not an object. * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. */ template - simdjson_inline error_code get(T &out) -#if SIMDJSON_SUPPORTS_DESERIALIZATION + simdjson_warn_unused simdjson_inline error_code get(T &out) +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept #endif { - #if SIMDJSON_SUPPORTS_DESERIALIZATION + #if SIMDJSON_SUPPORTS_CONCEPTS if constexpr (custom_deserializable) { return deserialize(*this, out); + } else if constexpr (concepts::optional_type) { + using value_type = typename std::remove_cvref_t::value_type; + + // Check if the value is null + bool is_null_value; + SIMDJSON_TRY( is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); // Set to nullopt + return SUCCESS; + } + + if (!out) { + out.emplace(); + } + return get(out.value()); } else { static_assert(!sizeof(T), "The get method with type T is not implemented by the simdjson library. " "And you do not seem to have added support for it. Indeed, we have that " @@ -101977,7 +116901,7 @@ class value { static_cast(out); // to get rid of unused errors return UNINITIALIZED; } -#else // SIMDJSON_SUPPORTS_DESERIALIZATION +#else // SIMDJSON_SUPPORTS_CONCEPTS // Unless the simdjson library or the user provides an inline implementation, calling this method should // immediately fail. static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " @@ -102095,7 +117019,7 @@ class value { * @returns INCORRECT_TYPE if the JSON value is not a string. Otherwise, we return SUCCESS. */ template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; /** * Cast this JSON value to a "wobbly" string. @@ -102616,7 +117540,7 @@ struct simdjson_result : public lsx::implementation_simdjs simdjson_inline simdjson_result get_double_in_string() noexcept; simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; simdjson_inline simdjson_result get_wobbly_string() noexcept; simdjson_inline simdjson_result get_raw_json_string() noexcept; simdjson_inline simdjson_result get_bool() noexcept; @@ -102697,7 +117621,22 @@ struct simdjson_result : public lsx::implementation_simdjs simdjson_result operator[](int) noexcept = delete; /** - * Get the type of this JSON value. + * Get the type of this JSON value. It does not validate or consume the value. + * E.g., you must still call "is_null()" to check that a value is null even if + * "type()" returns json_type::null. + * + * Given a valid JSON document, the answer can be one of + * simdjson::ondemand::json_type::object, + * simdjson::ondemand::json_type::array, + * simdjson::ondemand::json_type::string, + * simdjson::ondemand::json_type::number, + * simdjson::ondemand::json_type::boolean, + * simdjson::ondemand::json_type::null. + * + * Starting with simdjson 4.0, this function will return simdjson::ondemand::json_type::unknown + * given a bad token. + * This allows you to identify a case such as {"key": NaN} and identify the NaN value. + * The simdjson::ondemand::json_type::unknown value should only happen with non-valid JSON. * * NOTE: If you're only expecting a value to be one type (a typical case), it's generally * better to just call .get_double, .get_string, etc. and check for INCORRECT_TYPE (or just @@ -103191,14 +118130,14 @@ class json_iterator { * @param error The error to report. Must not be SUCCESS, UNINITIALIZED, INCORRECT_TYPE, or NO_SUCH_FIELD. * @param message An error message to report with the error. */ - simdjson_inline error_code report_error(error_code error, const char *message) noexcept; + simdjson_warn_unused simdjson_inline error_code report_error(error_code error, const char *message) noexcept; /** * Log error, but don't stop iteration. * @param error The error to report. Must be INCORRECT_TYPE, or NO_SUCH_FIELD. * @param message An error message to report with the error. */ - simdjson_inline error_code optional_error(error_code error, const char *message) noexcept; + simdjson_warn_unused simdjson_inline error_code optional_error(error_code error, const char *message) noexcept; /** * Take an input in json containing max_len characters and attempt to copy it over to tmpbuf, a buffer with @@ -103218,7 +118157,7 @@ class json_iterator { simdjson_inline void reenter_child(token_position position, depth_t child_depth) noexcept; - simdjson_inline error_code consume_character(char c) noexcept; + simdjson_warn_unused simdjson_inline error_code consume_character(char c) noexcept; #if SIMDJSON_DEVELOPMENT_CHECKS simdjson_inline token_position start_position(depth_t depth) const noexcept; simdjson_inline void set_start_position(depth_t depth, token_position position) noexcept; @@ -103309,6 +118248,7 @@ namespace ondemand { * The type of a JSON value. */ enum class json_type { + unknown=0, // Start at 1 to catch uninitialized / default values more easily array=1, ///< A JSON array ( [ 1, 2, 3 ... ] ) object, ///< A JSON object ( { "a": 1, "b" 2, ... } ) @@ -103515,6 +118455,12 @@ class raw_json_string { */ simdjson_inline const char * raw() const noexcept; + /** + * Get the character at index i. This is unchecked. + * [0] when the string is of length 0 returns the final quote ("). + */ + simdjson_inline char operator[](size_t i) const noexcept; + /** * This compares the current instance to the std::string_view target: returns true if * they are byte-by-byte equal (no escaping is done) on target.size() characters, @@ -103654,10 +118600,10 @@ struct simdjson_result : public lsx::implementat simdjson_inline ~simdjson_result() noexcept = default; ///< @private simdjson_inline simdjson_result raw() const noexcept; + simdjson_inline char operator[](size_t) const noexcept; simdjson_inline simdjson_warn_unused simdjson_result unescape(lsx::ondemand::json_iterator &iter, bool allow_replacement) const noexcept; simdjson_inline simdjson_warn_unused simdjson_result unescape_wobbly(lsx::ondemand::json_iterator &iter) const noexcept; }; - } // namespace simdjson #endif // SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_H @@ -103673,6 +118619,7 @@ struct simdjson_result : public lsx::implementat /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ #include +#include namespace simdjson { namespace lsx { @@ -103791,7 +118738,9 @@ class parser { simdjson_warn_unused simdjson_result iterate(std::string_view json, size_t capacity) & noexcept; /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ simdjson_warn_unused simdjson_result iterate(const std::string &json) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + /** @overload simdjson_result iterate(padded_string_view json) & noexcept + The string instance might be have its capacity extended. Note that this can still + result in AddressSanitizer: container-overflow in some cases. */ simdjson_warn_unused simdjson_result iterate(std::string &json) & noexcept; /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ simdjson_warn_unused simdjson_result iterate(const simdjson_result &json) & noexcept; @@ -103879,6 +118828,11 @@ class parser { * Setting batch_size to excessively large or excessively small values may impact negatively the * performance. * + * ### Threads + * + * When compiled with SIMDJSON_THREADS_ENABLED, this method will use a single thread under the + * hood to do some lookahead. + * * ### REQUIRED: Buffer Padding * * The buffer must have at least SIMDJSON_PADDING extra allocated bytes. It does not matter what @@ -103886,10 +118840,10 @@ class parser { * using a sanitizer that verifies that no uninitialized byte is read, then you should initialize the * SIMDJSON_PADDING bytes to avoid runtime warnings. * - * ### Threads + * This is checked automatically with all iterate_many function calls, except for the two + * that take pointers (const char* or const uint8_t*). * - * When compiled with SIMDJSON_THREADS_ENABLED, this method will use a single thread under the - * hood to do some lookahead. + * ### Threads * * ### Parser Capacity * @@ -103915,14 +118869,16 @@ class parser { */ inline simdjson_result iterate_many(const uint8_t *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ + inline simdjson_result iterate_many(padded_string_view json, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ inline simdjson_result iterate_many(const char *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ inline simdjson_result iterate_many(const std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; - inline simdjson_result iterate_many(const std::string &&s, size_t batch_size, bool allow_comma_separated = false) = delete;// unsafe + /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) + the string might be automatically padded with up to SIMDJSON_PADDING whitespace characters */ + inline simdjson_result iterate_many(std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ inline simdjson_result iterate_many(const padded_string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; - inline simdjson_result iterate_many(const padded_string &&s, size_t batch_size, bool allow_comma_separated = false) = delete;// unsafe - /** @private We do not want to allow implicit conversion from C string to std::string. */ simdjson_result iterate_many(const char *buf, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept = delete; @@ -104024,13 +118980,39 @@ class parser { bool string_buffer_overflow(const uint8_t *string_buf_loc) const noexcept; #endif + /** + * Get a unique parser instance corresponding to the current thread. + * This instance can be safely used within the current thread, but it should + * not be passed to other threads. + * + * A parser should only be used for one document at a time. + * + * Our simdjson::from functions use this parser instance. + * + * You can free the related parser by calling release_parser(). + */ + static simdjson_inline simdjson_warn_unused ondemand::parser& get_parser(); + /** + * Release the parser instance initialized by get_parser() and all the + * associated resources (memory). Returns true if a parser instance + * was released. + */ + static simdjson_inline bool release_parser(); + private: + friend bool release_parser(); + friend ondemand::parser& get_parser(); + /** Get the thread-local parser instance, allocates it if needed */ + static simdjson_inline simdjson_warn_unused std::unique_ptr& get_parser_instance(); + /** Get the thread-local parser instance, it might be null */ + static simdjson_inline simdjson_warn_unused std::unique_ptr& get_threadlocal_parser_if_exists(); /** @private [for benchmarking access] The implementation to use */ std::unique_ptr implementation{}; size_t _capacity{0}; size_t _max_capacity; size_t _max_depth{DEFAULT_MAX_DEPTH}; std::unique_ptr string_buf{}; + #if SIMDJSON_DEVELOPMENT_CHECKS std::unique_ptr start_positions{}; #endif @@ -104058,6 +119040,315 @@ struct simdjson_result : public lsx::implementation_simdj #endif // SIMDJSON_GENERIC_ONDEMAND_PARSER_H /* end file simdjson/generic/ondemand/parser.h for lsx */ +// JSON builder - needed for extract_into functionality +/* including simdjson/generic/ondemand/json_string_builder.h for lsx: #include "simdjson/generic/ondemand/json_string_builder.h" */ +/* begin file simdjson/generic/ondemand/json_string_builder.h for lsx */ +/** + * This file is part of the builder API. It is temporarily in the ondemand directory + * but we will move it to a builder directory later. + */ +#ifndef SIMDJSON_GENERIC_STRING_BUILDER_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { + + +#if SIMDJSON_SUPPORTS_CONCEPTS + +namespace lsx { +namespace builder { + class string_builder; +}} + +template +struct has_custom_serialization : std::false_type {}; + +inline constexpr struct serialize_tag { + template + requires custom_deserializable + constexpr void operator()(lsx::builder::string_builder& b, T& obj) const{ + return tag_invoke(*this, b, obj); + } + + +} serialize{}; +template +struct has_custom_serialization(), std::declval())) +>> : std::true_type {}; + +template +constexpr bool require_custom_serialization = has_custom_serialization::value; +#else +struct has_custom_serialization : std::false_type {}; +#endif // SIMDJSON_SUPPORTS_CONCEPTS + +namespace lsx { +namespace builder { +/** + * A builder for JSON strings representing documents. This is a low-level + * builder that is not meant to be used directly by end-users. Though it + * supports atomic types (Booleans, strings), it does not support composed + * types (arrays and objects). + * + * Ultimately, this class can support kernel-specific optimizations. E.g., + * it may make use of SIMD instructions to escape strings faster. + */ +class string_builder { +public: + simdjson_inline string_builder(size_t initial_capacity = DEFAULT_INITIAL_CAPACITY); + + static constexpr size_t DEFAULT_INITIAL_CAPACITY = 1024; + + /** + * Append number (includes Booleans). Booleans are mapped to the strings + * false and true. Numbers are converted to strings abiding by the JSON standard. + * Floating-point numbers are converted to the shortest string that 'correctly' + * represents the number. + */ + template::value>::type> + simdjson_inline void append(number_type v) noexcept; + + /** + * Append character c. + */ + simdjson_inline void append(char c) noexcept; + + /** + * Append the string 'null'. + */ + simdjson_inline void append_null() noexcept; + + /** + * Clear the content. + */ + simdjson_inline void clear() noexcept; + + /** + * Append the std::string_view, after escaping it. + * There is no UTF-8 validation. + */ + simdjson_inline void escape_and_append(std::string_view input) noexcept; + + /** + * Append the std::string_view surrounded by double quotes, after escaping it. + * There is no UTF-8 validation. + */ + simdjson_inline void escape_and_append_with_quotes(std::string_view input) noexcept; +#if SIMDJSON_SUPPORTS_CONCEPTS + template + simdjson_inline void escape_and_append_with_quotes() noexcept; +#endif + /** + * Append the character surrounded by double quotes, after escaping it. + * There is no UTF-8 validation. + */ + simdjson_inline void escape_and_append_with_quotes(char input) noexcept; + + /** + * Append the character surrounded by double quotes, after escaping it. + * There is no UTF-8 validation. + */ + simdjson_inline void escape_and_append_with_quotes(const char* input) noexcept; + + /** + * Append the C string directly, without escaping. + * There is no UTF-8 validation. + */ + simdjson_inline void append_raw(const char *c) noexcept; + + /** + * Append "{" to the buffer. + */ + simdjson_inline void start_object() noexcept; + + /** + * Append "}" to the buffer. + */ + simdjson_inline void end_object() noexcept; + + /** + * Append "[" to the buffer. + */ + simdjson_inline void start_array() noexcept; + + /** + * Append "]" to the buffer. + */ + simdjson_inline void end_array() noexcept; + + /** + * Append "," to the buffer. + */ + simdjson_inline void append_comma() noexcept; + + /** + * Append ":" to the buffer. + */ + simdjson_inline void append_colon() noexcept; + + /** + * Append a key-value pair to the buffer. + * The key is escaped and surrounded by double quotes. + * The value is escaped if it is a string. + */ + template + simdjson_inline void append_key_value(key_type key, value_type value) noexcept; +#if SIMDJSON_SUPPORTS_CONCEPTS + template + simdjson_inline void append_key_value(value_type value) noexcept; + + // Support for optional types (std::optional, etc.) + template + requires(!require_custom_serialization) + simdjson_inline void append(const T &opt); + + template + requires(require_custom_serialization) + simdjson_inline void append(const T &val); + + // Support for string-like types + template + requires(std::is_convertible::value || + std::is_same::value ) + simdjson_inline void append(const T &value); +#endif +#if SIMDJSON_SUPPORTS_RANGES && SIMDJSON_SUPPORTS_CONCEPTS + // Support for range-based appending (std::ranges::view, etc.) + template +requires (!std::is_convertible::value) + simdjson_inline void append(const R &range) noexcept; +#endif + /** + * Append the std::string_view directly, without escaping. + * There is no UTF-8 validation. + */ + simdjson_inline void append_raw(std::string_view input) noexcept; + + /** + * Append len characters from str. + * There is no UTF-8 validation. + */ + simdjson_inline void append_raw(const char *str, size_t len) noexcept; +#if SIMDJSON_EXCEPTIONS + /** + * Creates an std::string from the written JSON buffer. + * Throws if memory allocation failed + * + * The result may not be valid UTF-8 if some of your content was not valid UTF-8. + * Use validate_unicode() to check the content if needed. + */ + simdjson_inline operator std::string() const noexcept(false); + + /** + * Creates an std::string_view from the written JSON buffer. + * Throws if memory allocation failed. + * + * The result may not be valid UTF-8 if some of your content was not valid UTF-8. + * Use validate_unicode() to check the content if needed. + */ + simdjson_inline operator std::string_view() const noexcept(false) simdjson_lifetime_bound; +#endif + + /** + * Returns a view on the written JSON buffer. Returns an error + * if memory allocation failed. + * + * The result may not be valid UTF-8 if some of your content was not valid UTF-8. + * Use validate_unicode() to check the content. + */ + simdjson_inline simdjson_result view() const noexcept; + + /** + * Appends the null character to the buffer and returns + * a pointer to the beginning of the written JSON buffer. + * Returns an error if memory allocation failed. + * The result is null-terminated. + * + * The result may not be valid UTF-8 if some of your content was not valid UTF-8. + * Use validate_unicode() to check the content. + */ + simdjson_inline simdjson_result c_str() noexcept; + + /** + * Return true if the content is valid UTF-8. + */ + simdjson_inline bool validate_unicode() const noexcept; + + /** + * Returns the current size of the written JSON buffer. + * If an error occurred, returns 0. + */ + simdjson_inline size_t size() const noexcept; + +private: + /** + * Returns true if we can write at least upcoming_bytes bytes. + * The underlying buffer is reallocated if needed. It is designed + * to be called before writing to the buffer. It should be fast. + */ + simdjson_inline bool capacity_check(size_t upcoming_bytes); + + /** + * Grow the buffer to at least desired_capacity bytes. + * If the allocation fails, is_valid is set to false. We expect + * that this function would not be repeatedly called. + */ + simdjson_inline void grow_buffer(size_t desired_capacity); + + /** + * We use this helper function to make sure that is_valid is kept consistent. + */ + simdjson_inline void set_valid(bool valid) noexcept; + + std::unique_ptr buffer{}; + size_t position{0}; + size_t capacity{0}; + bool is_valid{true}; +}; + + + +} +} + + +#if !SIMDJSON_STATIC_REFLECTION +// fallback implementation until we have static reflection +template +simdjson_warn_unused simdjson_result to_json(const Z &z, size_t initial_capacity = simdjson::lsx::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { + simdjson::lsx::builder::string_builder b(initial_capacity); + b.append(z); + std::string_view s; + auto e = b.view().get(s); + if(e) { return e; } + return std::string(s); +} +template +simdjson_warn_unused simdjson_error to_json(const Z &z, std::string &s, size_t initial_capacity = simdjson::lsx::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { + simdjson::lsx::builder::string_builder b(initial_capacity); + b.append(z); + std::string_view sv; + auto e = b.view().get(sv); + if(e) { return e; } + s.assign(sv.data(), sv.size()); + return simdjson::SUCCESS; +} +#endif + +#if SIMDJSON_SUPPORTS_CONCEPTS +#endif // SIMDJSON_SUPPORTS_CONCEPTS + +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_STRING_BUILDER_H +/* end file simdjson/generic/ondemand/json_string_builder.h for lsx */ + // All other declarations /* including simdjson/generic/ondemand/array.h for lsx: #include "simdjson/generic/ondemand/array.h" */ /* begin file simdjson/generic/ondemand/array.h for lsx */ @@ -104194,11 +119485,42 @@ class array { * - INDEX_OUT_OF_BOUNDS if the array index is larger than an array length */ simdjson_inline simdjson_result at(size_t index) noexcept; + +#if SIMDJSON_SUPPORTS_CONCEPTS + /** + * Get this array as the given type. + * + * @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized. + * @returns INCORRECT_TYPE If the JSON array is not of the given type. + * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. + */ + template + simdjson_warn_unused simdjson_inline error_code get(T &out) + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) { + static_assert(custom_deserializable); + return deserialize(*this, out); + } + /** + * Get this array as the given type. + * + * @returns A value of the given type, parsed from the JSON. + * @returns INCORRECT_TYPE If the JSON value is not the given type. + */ + template + simdjson_inline simdjson_result get() + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) + { + static_assert(std::is_default_constructible::value, "The specified type is not default constructible."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } +#endif // SIMDJSON_SUPPORTS_CONCEPTS protected: /** * Go to the end of the array, no matter where you are right now. */ - simdjson_inline error_code consume() noexcept; + simdjson_warn_unused simdjson_inline error_code consume() noexcept; /** * Begin array iteration. @@ -104272,7 +119594,28 @@ struct simdjson_result : public lsx::implementation_simdjs simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; simdjson_inline simdjson_result raw_json() noexcept; +#if SIMDJSON_SUPPORTS_CONCEPTS + // TODO: move this code into object-inl.h + template + simdjson_inline simdjson_result get() noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + return first; + } + return first.get(); + } + template + simdjson_warn_unused simdjson_inline error_code get(T& out) noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + out = first; + } else { + SIMDJSON_TRY( first.get(out) ); + } + return SUCCESS; + } +#endif // SIMDJSON_SUPPORTS_CONCEPTS }; } // namespace simdjson @@ -104317,7 +119660,8 @@ class array_iterator { * * Part of the std::iterator interface. */ - simdjson_inline simdjson_result operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. + simdjson_inline simdjson_result + operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. /** * Check if we are at the end of the JSON. * @@ -104341,6 +119685,11 @@ class array_iterator { */ simdjson_inline array_iterator &operator++() noexcept; + /** + * Check if the array is at the end. + */ + simdjson_warn_unused simdjson_inline bool at_end() const noexcept; + private: value_iterator iter{}; @@ -104359,7 +119708,6 @@ namespace simdjson { template<> struct simdjson_result : public lsx::implementation_simdjson_result_base { -public: simdjson_inline simdjson_result(lsx::ondemand::array_iterator &&value) noexcept; ///< @private simdjson_inline simdjson_result(error_code error) noexcept; ///< @private simdjson_inline simdjson_result() noexcept = default; @@ -104372,6 +119720,8 @@ struct simdjson_result : public lsx::implementati simdjson_inline bool operator==(const simdjson_result &) const noexcept; simdjson_inline bool operator!=(const simdjson_result &) const noexcept; simdjson_inline simdjson_result &operator++() noexcept; + + simdjson_warn_unused simdjson_inline bool at_end() const noexcept; }; } // namespace simdjson @@ -104499,7 +119849,7 @@ class document { * @returns INCORRECT_TYPE if the JSON value is not a string. Otherwise, we return SUCCESS. */ template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; /** * Cast this JSON value to a string. * @@ -104565,7 +119915,7 @@ class document { */ template simdjson_inline simdjson_result get() & -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept @@ -104588,7 +119938,7 @@ class document { */ template simdjson_inline simdjson_result get() && -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept @@ -104606,18 +119956,18 @@ class document { * Be mindful that the document instance must remain in scope while you are accessing object, array and value instances. * * @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized. - * @returns INCORRECT_TYPE If the JSON value is not an object. + * @returns INCORRECT_TYPE If the JSON value is of the given type. * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. */ template - simdjson_inline error_code get(T &out) & -#if SIMDJSON_SUPPORTS_DESERIALIZATION + simdjson_warn_unused simdjson_inline error_code get(T &out) & +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept #endif { -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS if constexpr (custom_deserializable) { return deserialize(*this, out); } else { @@ -104629,7 +119979,7 @@ class document { static_cast(out); // to get rid of unused errors return UNINITIALIZED; } -#else // SIMDJSON_SUPPORTS_DESERIALIZATION +#else // SIMDJSON_SUPPORTS_CONCEPTS // Unless the simdjson library or the user provides an inline implementation, calling this method should // immediately fail. static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " @@ -104639,7 +119989,7 @@ class document { " You may also add support for custom types, see our documentation."); static_cast(out); // to get rid of unused errors return UNINITIALIZED; -#endif // SIMDJSON_SUPPORTS_DESERIALIZATION +#endif // SIMDJSON_SUPPORTS_CONCEPTS } /** @overload template error_code get(T &out) & noexcept */ @@ -104704,7 +120054,7 @@ class document { * time it parses a document or when it is destroyed. * @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string. */ - simdjson_inline operator std::string_view() noexcept(false); + simdjson_inline operator std::string_view() noexcept(false) simdjson_lifetime_bound; /** * Cast this JSON value to a raw_json_string. * @@ -104713,7 +120063,7 @@ class document { * @returns A pointer to the raw JSON for the given string. * @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string. */ - simdjson_inline operator raw_json_string() noexcept(false); + simdjson_inline operator raw_json_string() noexcept(false) simdjson_lifetime_bound; /** * Cast this JSON value to a bool. * @@ -104863,11 +120213,27 @@ class document { * E.g., you must still call "is_null()" to check that a value is null even if * "type()" returns json_type::null. * + * The answer can be one of + * simdjson::ondemand::json_type::object, + * simdjson::ondemand::json_type::array, + * simdjson::ondemand::json_type::string, + * simdjson::ondemand::json_type::number, + * simdjson::ondemand::json_type::boolean, + * simdjson::ondemand::json_type::null. + * + * Starting with simdjson 4.0, this function will return simdjson::ondemand::json_type::unknown + * given a bad token. + * This allows you to identify a case such as {"key": NaN} and identify the NaN value. + * The simdjson::ondemand::json_type::unknown value should only happen with non-valid JSON. + * * NOTE: If you're only expecting a value to be one type (a typical case), it's generally * better to just call .get_double, .get_string, etc. and check for INCORRECT_TYPE (or just * let it throw an exception). * - * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". + * Prior to simdjson 4.0, this function would return an error given a bad token. + * Starting with simdjson 4.0, it will return simdjson::ondemand::json_type::unknown. + * This allows you to identify a case such as {"key": NaN} and identify the NaN value. + * The simdjson::ondemand::json_type::unknown value should only happen with non-valid JSON. */ simdjson_inline simdjson_result type() noexcept; @@ -105091,11 +120457,41 @@ class document { * the JSON document. */ simdjson_inline simdjson_result raw_json() noexcept; + +#if SIMDJSON_STATIC_REFLECTION + /** + * Extract only specific fields from the JSON object into a struct. + * + * This allows selective deserialization of only the fields you need, + * potentially improving performance by skipping unwanted fields. + * + * Example: + * ```c++ + * struct Car { + * std::string make; + * std::string model; + * int year; + * double price; + * }; + * + * Car car; + * doc.extract_into<"make", "model">(car); + * // Only 'make' and 'model' fields are extracted from JSON + * ``` + * + * @tparam FieldNames Compile-time string literals specifying which fields to extract + * @param out The output struct to populate with selected fields + * @returns SUCCESS on success, or an error code if a required field is missing or has wrong type + */ + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept; +#endif // SIMDJSON_STATIC_REFLECTION protected: /** * Consumes the document. */ - simdjson_inline error_code consume() noexcept; + simdjson_warn_unused simdjson_inline error_code consume() noexcept; simdjson_inline document(ondemand::json_iterator &&iter) noexcept; simdjson_inline const uint8_t *text(uint32_t idx) const noexcept; @@ -105148,7 +120544,7 @@ class document_reference { simdjson_inline simdjson_result get_double_in_string() noexcept; simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; simdjson_inline simdjson_result get_wobbly_string() noexcept; simdjson_inline simdjson_result get_raw_json_string() noexcept; simdjson_inline simdjson_result get_bool() noexcept; @@ -105157,7 +120553,7 @@ class document_reference { simdjson_inline simdjson_result is_null() noexcept; template simdjson_inline simdjson_result get() & -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept @@ -105170,7 +120566,7 @@ class document_reference { } template simdjson_inline simdjson_result get() && -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept @@ -105192,14 +120588,14 @@ class document_reference { * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. */ template - simdjson_inline error_code get(T &out) & -#if SIMDJSON_SUPPORTS_DESERIALIZATION + simdjson_warn_unused simdjson_inline error_code get(T &out) & +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept #endif { -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS if constexpr (custom_deserializable) { return deserialize(*this, out); } else { @@ -105211,7 +120607,7 @@ class document_reference { static_cast(out); // to get rid of unused errors return UNINITIALIZED; } -#else // SIMDJSON_SUPPORTS_DESERIALIZATION +#else // SIMDJSON_SUPPORTS_CONCEPTS // Unless the simdjson library or the user provides an inline implementation, calling this method should // immediately fail. static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " @@ -105221,12 +120617,17 @@ class document_reference { " You may also add support for custom types, see our documentation."); static_cast(out); // to get rid of unused errors return UNINITIALIZED; -#endif // SIMDJSON_SUPPORTS_DESERIALIZATION +#endif // SIMDJSON_SUPPORTS_CONCEPTS } /** @overload template error_code get(T &out) & noexcept */ template simdjson_inline error_code get(T &out) && noexcept; simdjson_inline simdjson_result raw_json() noexcept; +#if SIMDJSON_STATIC_REFLECTION + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept; +#endif // SIMDJSON_STATIC_REFLECTION simdjson_inline operator document&() const noexcept; #if SIMDJSON_EXCEPTIONS template @@ -105295,7 +120696,7 @@ struct simdjson_result : public lsx::implementation_sim simdjson_inline simdjson_result get_double_in_string() noexcept; simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; simdjson_inline simdjson_result get_wobbly_string() noexcept; simdjson_inline simdjson_result get_raw_json_string() noexcept; simdjson_inline simdjson_result get_bool() noexcept; @@ -105308,6 +120709,9 @@ struct simdjson_result : public lsx::implementation_sim template simdjson_inline error_code get(T &out) & noexcept; template simdjson_inline error_code get(T &out) && noexcept; #if SIMDJSON_EXCEPTIONS + + using lsx::implementation_simdjson_result_base::operator*; + using lsx::implementation_simdjson_result_base::operator->; template ::value == false>::type> explicit simdjson_inline operator T() noexcept(false); simdjson_inline operator lsx::ondemand::array() & noexcept(false); @@ -105347,6 +120751,11 @@ struct simdjson_result : public lsx::implementation_sim simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; +#if SIMDJSON_STATIC_REFLECTION + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept; +#endif // SIMDJSON_STATIC_REFLECTION }; @@ -105373,7 +120782,7 @@ struct simdjson_result : public lsx::implemen simdjson_inline simdjson_result get_double_in_string() noexcept; simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; simdjson_inline simdjson_result get_wobbly_string() noexcept; simdjson_inline simdjson_result get_raw_json_string() noexcept; simdjson_inline simdjson_result get_bool() noexcept; @@ -105424,6 +120833,11 @@ struct simdjson_result : public lsx::implemen simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; +#if SIMDJSON_STATIC_REFLECTION + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept; +#endif // SIMDJSON_STATIC_REFLECTION }; @@ -105566,6 +120980,7 @@ class document_stream { * Default constructor. */ simdjson_inline iterator() noexcept; + simdjson_inline iterator(const iterator &other) noexcept = default; /** * Get the current document (or error). */ @@ -105579,6 +120994,7 @@ class document_stream { * @param other the end iterator to compare to. */ simdjson_inline bool operator!=(const iterator &other) const noexcept; + simdjson_inline bool operator==(const iterator &other) const noexcept; /** * @private * @@ -105622,6 +121038,11 @@ class document_stream { */ inline error_code error() const noexcept; + /** + * Returns whether the iterator is at the end. + */ + inline bool at_end() const noexcept; + private: simdjson_inline iterator(document_stream *s, bool finished) noexcept; /** The document_stream we're iterating through. */ @@ -105633,6 +121054,7 @@ class document_stream { friend class document_stream; friend class json_iterator; }; + using iterator = document_stream::iterator; /** * Start iterating the documents in the stream. @@ -105896,6 +121318,9 @@ struct simdjson_result : public lsx::implementation_simdjs /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */ +/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION && SIMDJSON_SUPPORTS_CONCEPTS */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_string_builder.h" // for constevalutil::fixed_string */ +/* amalgamation skipped (editor-only): #endif */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { @@ -106093,11 +121518,71 @@ class object { */ simdjson_inline simdjson_result raw_json() noexcept; +#if SIMDJSON_SUPPORTS_CONCEPTS + /** + * Get this object as the given type. + * + * @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized. + * @returns INCORRECT_TYPE If the JSON object is not of the given type. + * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. + */ + template + simdjson_warn_unused simdjson_inline error_code get(T &out) + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) { + static_assert(custom_deserializable); + return deserialize(*this, out); + } + /** + * Get this array as the given type. + * + * @returns A value of the given type, parsed from the JSON. + * @returns INCORRECT_TYPE If the JSON value is not the given type. + */ + template + simdjson_inline simdjson_result get() + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) + { + static_assert(std::is_default_constructible::value, "The specified type is not default constructible."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } + +#if SIMDJSON_STATIC_REFLECTION + /** + * Extract only specific fields from the JSON object into a struct. + * + * This allows selective deserialization of only the fields you need, + * potentially improving performance by skipping unwanted fields. + * + * Example: + * ```c++ + * struct Car { + * std::string make; + * std::string model; + * int year; + * double price; + * }; + * + * Car car; + * object.extract_into<"make", "model">(car); + * // Only 'make' and 'model' fields are extracted from JSON + * ``` + * + * @tparam FieldNames Compile-time string literals specifying which fields to extract + * @param out The output struct to populate with selected fields + * @returns SUCCESS on success, or an error code if a required field is missing or has wrong type + */ + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept; +#endif // SIMDJSON_STATIC_REFLECTION +#endif // SIMDJSON_SUPPORTS_CONCEPTS protected: /** * Go to the end of the object, no matter where you are right now. */ - simdjson_inline error_code consume() noexcept; + simdjson_warn_unused simdjson_inline error_code consume() noexcept; static simdjson_inline simdjson_result start(value_iterator &iter) noexcept; static simdjson_inline simdjson_result start_root(value_iterator &iter) noexcept; static simdjson_inline simdjson_result started(value_iterator &iter) noexcept; @@ -106136,12 +121621,42 @@ struct simdjson_result : public lsx::implementation_simdj simdjson_inline simdjson_result operator[](std::string_view key) && noexcept; simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; - inline simdjson_result reset() noexcept; inline simdjson_result is_empty() noexcept; inline simdjson_result count_fields() & noexcept; inline simdjson_result raw_json() noexcept; + #if SIMDJSON_SUPPORTS_CONCEPTS + // TODO: move this code into object-inl.h + template + simdjson_inline simdjson_result get() noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + return first; + } + return first.get(); + } + template + simdjson_warn_unused simdjson_inline error_code get(T& out) noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + out = first; + } else { + SIMDJSON_TRY( first.get(out) ); + } + return SUCCESS; + } + +#if SIMDJSON_STATIC_REFLECTION + // TODO: move this code into object-inl.h + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) noexcept { + if (error()) { return error(); } + return first.extract_into(out); + } +#endif // SIMDJSON_STATIC_REFLECTION +#endif // SIMDJSON_SUPPORTS_CONCEPTS }; } // namespace simdjson @@ -106270,6 +121785,20 @@ inline simdjson_result to_json_string(simdjson_result to_json_string(simdjson_result x); inline simdjson_result to_json_string(simdjson_result x); inline simdjson_result to_json_string(simdjson_result x); + +#if SIMDJSON_STATIC_REFLECTION +/** + * Create a JSON string from any user-defined type using static reflection. + * Only available when SIMDJSON_STATIC_REFLECTION is enabled. + */ +template + requires(!std::same_as && + !std::same_as && + !std::same_as && + !std::same_as) +inline std::string to_json_string(const T& obj); +#endif + } // namespace simdjson /** @@ -106341,28 +121870,30 @@ inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result #include +#if SIMDJSON_STATIC_REFLECTION +#include +// #include // for std::define_static_string - header not available yet +#endif namespace simdjson { -template -constexpr bool require_custom_serialization = false; ////////////////////////////// // Number deserialization ////////////////////////////// template - requires(!require_custom_serialization) error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { using limits = std::numeric_limits; @@ -106376,7 +121907,6 @@ error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { } template - requires(!require_custom_serialization) error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { double x; SIMDJSON_TRY(val.get_double().get(x)); @@ -106385,7 +121915,6 @@ error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { } template - requires(!require_custom_serialization) error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { using limits = std::numeric_limits; @@ -106398,8 +121927,23 @@ error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { return SUCCESS; } +////////////////////////////// +// String deserialization +////////////////////////////// + +// just a character! +error_code tag_invoke(deserialize_tag, auto &val, char &out) noexcept { + std::string_view x; + SIMDJSON_TRY(val.get_string().get(x)); + if(x.size() != 1) { + return INCORRECT_TYPE; + } + out = x[0]; + return SUCCESS; +} + +// any string-like type (can be constructed from std::string_view) template - requires(!require_custom_serialization) error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(std::is_nothrow_constructible_v) { std::string_view str; SIMDJSON_TRY(val.get_string().get(str)); @@ -106416,7 +121960,6 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(std::is_nothr * doc.get>(). */ template - requires(!require_custom_serialization) error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { using value_type = typename std::remove_cvref_t::value_type; static_assert( @@ -106425,9 +121968,13 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { static_assert( std::is_default_constructible_v, "The specified type inside the container must default constructible."); - lsx::ondemand::array arr; - SIMDJSON_TRY(val.get_array().get(arr)); + if constexpr (std::is_same_v, lsx::ondemand::array>) { + arr = val; + } else { + SIMDJSON_TRY(val.get_array().get(arr)); + } + for (auto v : arr) { if constexpr (concepts::returns_reference) { if (auto const err = v.get().get(concepts::emplace_one(out)); @@ -106458,7 +122005,6 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { * string-keyed types. */ template - requires(!require_custom_serialization) error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { using value_type = typename std::remove_cvref_t::mapped_type; static_assert( @@ -106484,7 +122030,45 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { return SUCCESS; } +template +error_code tag_invoke(deserialize_tag, lsx::ondemand::object &obj, T &out) noexcept { + using value_type = typename std::remove_cvref_t::mapped_type; + out.clear(); + for (auto field : obj) { + std::string_view key; + SIMDJSON_TRY(field.unescaped_key().get(key)); + + lsx::ondemand::value value_obj; + SIMDJSON_TRY(field.value().get(value_obj)); + + value_type this_value; + SIMDJSON_TRY(value_obj.get(this_value)); + out.emplace(typename T::key_type(key), std::move(this_value)); + } + return SUCCESS; +} + +template +error_code tag_invoke(deserialize_tag, lsx::ondemand::value &val, T &out) noexcept { + lsx::ondemand::object obj; + SIMDJSON_TRY(val.get_object().get(obj)); + return simdjson::deserialize(obj, out); +} + +template +error_code tag_invoke(deserialize_tag, lsx::ondemand::document &doc, T &out) noexcept { + lsx::ondemand::object obj; + SIMDJSON_TRY(doc.get_object().get(obj)); + return simdjson::deserialize(obj, out); +} + +template +error_code tag_invoke(deserialize_tag, lsx::ondemand::document_reference &doc, T &out) noexcept { + lsx::ondemand::object obj; + SIMDJSON_TRY(doc.get_object().get(obj)); + return simdjson::deserialize(obj, out); +} /** @@ -106502,7 +122086,6 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { * @return status of the conversion */ template - requires(!require_custom_serialization) error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deserializable::element_type, ValT>) { using element_type = typename std::remove_cvref_t::element_type; @@ -106527,17 +122110,17 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deser /** * This CPO (Customization Point Object) will help deserialize into optional types. */ -template - requires(!require_custom_serialization) -error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deserializable::value_type, ValT>) { +template +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept(nothrow_deserializable::value_type, decltype(val)>) { using value_type = typename std::remove_cvref_t::value_type; - static_assert( - deserializable, - "The specified type inside the unique_ptr must itself be deserializable"); - static_assert( - std::is_default_constructible_v, - "The specified type inside the unique_ptr must default constructible."); + // Check if the value is null + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); // Set to nullopt + return SUCCESS; + } if (!out) { out.emplace(); @@ -106546,10 +122129,329 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deser return SUCCESS; } + +#if SIMDJSON_STATIC_REFLECTION + + +template +constexpr bool user_defined_type = (std::is_class_v +&& !std::is_same_v && !std::is_same_v && !concepts::optional_type && +!concepts::appendable_containers); + + +template + requires(user_defined_type && std::is_class_v) +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept { + lsx::ondemand::object obj; + if constexpr (std::is_same_v, lsx::ondemand::object>) { + obj = val; + } else { + SIMDJSON_TRY(val.get_object().get(obj)); + } + template for (constexpr auto mem : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) { + if constexpr (!std::meta::is_const(mem) && std::meta::is_public(mem)) { + constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem)); + if constexpr (concepts::optional_type) { + // for optional members, it's ok if the key is missing + auto error = obj[key].get(out.[:mem:]); + if (error && error != NO_SUCH_FIELD) { + if(error == NO_SUCH_FIELD) { + out.[:mem:].reset(); + continue; + } + return error; + } + } else { + // for non-optional members, the key must be present + SIMDJSON_TRY(obj[key].get(out.[:mem:])); + } + } + }; + return simdjson::SUCCESS; +} + +// Support for enum deserialization - deserialize from string representation using expand approach from P2996R12 +template + requires(std::is_enum_v) +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept { +#if SIMDJSON_STATIC_REFLECTION + std::string_view str; + SIMDJSON_TRY(val.get_string().get(str)); + constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T)); + template for (constexpr auto enum_val : enumerators) { + if (str == std::meta::identifier_of(enum_val)) { + out = [:enum_val:]; + return SUCCESS; + } + }; + + return INCORRECT_TYPE; +#else + // Fallback: deserialize as integer if reflection not available + std::underlying_type_t int_val; + SIMDJSON_TRY(val.get(int_val)); + out = static_cast(int_val); + return SUCCESS; +#endif +} + +template + requires(user_defined_type>) +error_code tag_invoke(deserialize_tag, simdjson_value &val, std::unique_ptr &out) noexcept { + if (!out) { + out = std::make_unique(); + if (!out) { + return MEMALLOC; + } + } + if (auto err = val.get(*out)) { + out.reset(); + return err; + } + return SUCCESS; +} + +template + requires(user_defined_type>) +error_code tag_invoke(deserialize_tag, simdjson_value &val, std::shared_ptr &out) noexcept { + if (!out) { + out = std::make_shared(); + if (!out) { + return MEMALLOC; + } + } + if (auto err = val.get(*out)) { + out.reset(); + return err; + } + return SUCCESS; +} + +#endif // SIMDJSON_STATIC_REFLECTION + +//////////////////////////////////////// +// Unique pointers +//////////////////////////////////////// +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_unique(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_bool().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_unique(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_int64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_unique(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_uint64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_unique(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_double().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_unique(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_string().get(*out)); + return SUCCESS; +} + + +//////////////////////////////////////// +// Shared pointers +//////////////////////////////////////// +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_shared(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_bool().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_shared(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_int64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_shared(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_uint64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_shared(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_double().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_shared(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_string().get(*out)); + return SUCCESS; +} + + +//////////////////////////////////////// +// Explicit optional specializations +//////////////////////////////////////// + +//////////////////////////////////////// +// Explicit smart pointer specializations for string and int types +//////////////////////////////////////// +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + // Check if the value is null + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); // Set to nullptr + return SUCCESS; + } + + if (!out) { + out = std::make_unique(); + } + std::string_view str; + SIMDJSON_TRY(val.get_string().get(str)); + *out = std::string{str}; + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + // Check if the value is null + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); // Set to nullptr + return SUCCESS; + } + + if (!out) { + out = std::make_shared(); + } + std::string_view str; + SIMDJSON_TRY(val.get_string().get(str)); + *out = std::string{str}; + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + // Check if the value is null + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); // Set to nullptr + return SUCCESS; + } + + if (!out) { + out = std::make_unique(); + } + int64_t temp; + SIMDJSON_TRY(val.get_int64().get(temp)); + *out = static_cast(temp); + return SUCCESS; +} + } // namespace simdjson #endif // SIMDJSON_ONDEMAND_DESERIALIZE_H -#endif // SIMDJSON_SUPPORTS_DESERIALIZATION +#endif // SIMDJSON_SUPPORTS_CONCEPTS /* end file simdjson/generic/ondemand/std_deserialize.h for lsx */ // Inline definitions @@ -106642,7 +122544,7 @@ simdjson_inline simdjson_result array::begin() noexcept { simdjson_inline simdjson_result array::end() noexcept { return array_iterator(iter); } -simdjson_inline error_code array::consume() noexcept { +simdjson_warn_unused simdjson_warn_unused simdjson_inline error_code array::consume() noexcept { auto error = iter.json_iter().skip_child(iter.depth()-1); if(error) { iter.abandon(); } return error; @@ -106833,6 +122735,9 @@ simdjson_inline array_iterator &array_iterator::operator++() noexcept { return *this; } +simdjson_inline bool array_iterator::at_end() const noexcept { + return iter.at_end(); +} } // namespace ondemand } // namespace lsx } // namespace simdjson @@ -106869,7 +122774,9 @@ simdjson_inline simdjson_result &simdjson_result< ++(first); return *this; } - +simdjson_inline bool simdjson_result::at_end() const noexcept { + return !first.iter.is_valid() || first.at_end(); +} } // namespace simdjson #endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_INL_H @@ -106926,7 +122833,7 @@ simdjson_inline simdjson_result value::get_string(bool allow_r return iter.get_string(allow_replacement); } template -simdjson_inline error_code value::get_string(string_type& receiver, bool allow_replacement) noexcept { +simdjson_warn_unused simdjson_inline error_code value::get_string(string_type& receiver, bool allow_replacement) noexcept { return iter.get_string(receiver, allow_replacement); } simdjson_inline simdjson_result value::get_wobbly_string() noexcept { @@ -106968,15 +122875,15 @@ template<> simdjson_inline simdjson_result value::get() noexcept { retu template<> simdjson_inline simdjson_result value::get() noexcept { return get_bool(); } -template<> simdjson_inline error_code value::get(array& out) noexcept { return get_array().get(out); } -template<> simdjson_inline error_code value::get(object& out) noexcept { return get_object().get(out); } -template<> simdjson_inline error_code value::get(raw_json_string& out) noexcept { return get_raw_json_string().get(out); } -template<> simdjson_inline error_code value::get(std::string_view& out) noexcept { return get_string(false).get(out); } -template<> simdjson_inline error_code value::get(number& out) noexcept { return get_number().get(out); } -template<> simdjson_inline error_code value::get(double& out) noexcept { return get_double().get(out); } -template<> simdjson_inline error_code value::get(uint64_t& out) noexcept { return get_uint64().get(out); } -template<> simdjson_inline error_code value::get(int64_t& out) noexcept { return get_int64().get(out); } -template<> simdjson_inline error_code value::get(bool& out) noexcept { return get_bool().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(array& out) noexcept { return get_array().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(object& out) noexcept { return get_object().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(raw_json_string& out) noexcept { return get_raw_json_string().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(std::string_view& out) noexcept { return get_string(false).get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(number& out) noexcept { return get_number().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(double& out) noexcept { return get_double().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(uint64_t& out) noexcept { return get_uint64().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(int64_t& out) noexcept { return get_int64().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(bool& out) noexcept { return get_bool().get(out); } #if SIMDJSON_EXCEPTIONS template @@ -107573,7 +123480,7 @@ simdjson_inline simdjson_result document::get_string(bool allo return get_root_value_iterator().get_root_string(true, allow_replacement); } template -simdjson_inline error_code document::get_string(string_type& receiver, bool allow_replacement) noexcept { +simdjson_warn_unused simdjson_inline error_code document::get_string(string_type& receiver, bool allow_replacement) noexcept { return get_root_value_iterator().get_root_string(receiver, true, allow_replacement); } simdjson_inline simdjson_result document::get_wobbly_string() noexcept { @@ -107599,15 +123506,15 @@ template<> simdjson_inline simdjson_result document::get() & noexcept { template<> simdjson_inline simdjson_result document::get() & noexcept { return get_bool(); } template<> simdjson_inline simdjson_result document::get() & noexcept { return get_value(); } -template<> simdjson_inline error_code document::get(array& out) & noexcept { return get_array().get(out); } -template<> simdjson_inline error_code document::get(object& out) & noexcept { return get_object().get(out); } -template<> simdjson_inline error_code document::get(raw_json_string& out) & noexcept { return get_raw_json_string().get(out); } -template<> simdjson_inline error_code document::get(std::string_view& out) & noexcept { return get_string(false).get(out); } -template<> simdjson_inline error_code document::get(double& out) & noexcept { return get_double().get(out); } -template<> simdjson_inline error_code document::get(uint64_t& out) & noexcept { return get_uint64().get(out); } -template<> simdjson_inline error_code document::get(int64_t& out) & noexcept { return get_int64().get(out); } -template<> simdjson_inline error_code document::get(bool& out) & noexcept { return get_bool().get(out); } -template<> simdjson_inline error_code document::get(value& out) & noexcept { return get_value().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(array& out) & noexcept { return get_array().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(object& out) & noexcept { return get_object().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(raw_json_string& out) & noexcept { return get_raw_json_string().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(std::string_view& out) & noexcept { return get_string(false).get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(double& out) & noexcept { return get_double().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(uint64_t& out) & noexcept { return get_uint64().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(int64_t& out) & noexcept { return get_int64().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(bool& out) & noexcept { return get_bool().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(value& out) & noexcept { return get_value().get(out); } template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_raw_json_string(); } template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_string(false); } @@ -107627,8 +123534,8 @@ simdjson_inline document::operator object() & noexcept(false) { return get_objec simdjson_inline document::operator uint64_t() noexcept(false) { return get_uint64(); } simdjson_inline document::operator int64_t() noexcept(false) { return get_int64(); } simdjson_inline document::operator double() noexcept(false) { return get_double(); } -simdjson_inline document::operator std::string_view() noexcept(false) { return get_string(false); } -simdjson_inline document::operator raw_json_string() noexcept(false) { return get_raw_json_string(); } +simdjson_inline document::operator std::string_view() noexcept(false) simdjson_lifetime_bound { return get_string(false); } +simdjson_inline document::operator raw_json_string() noexcept(false) simdjson_lifetime_bound { return get_raw_json_string(); } simdjson_inline document::operator bool() noexcept(false) { return get_bool(); } simdjson_inline document::operator value() noexcept(false) { return get_value(); } @@ -107677,7 +123584,7 @@ simdjson_inline simdjson_result document::operator[](const char *key) & n return start_or_resume_object()[key]; } -simdjson_inline error_code document::consume() noexcept { +simdjson_warn_unused simdjson_inline error_code document::consume() noexcept { bool scalar = false; auto error = is_scalar().get(scalar); if(error) { return error; } @@ -107779,6 +123686,54 @@ simdjson_inline simdjson_result document::at_path(std::string_view json_p } } + + +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION + +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code document::extract_into(T& out) & noexcept { + // Helper to check if a field name matches any of the requested fields + auto should_extract = [](std::string_view field_name) constexpr -> bool { + return ((FieldNames.view() == field_name) || ...); + }; + + // Iterate through all members of T using reflection + template for (constexpr auto mem : std::define_static_array( + std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) { + + if constexpr (!std::meta::is_const(mem) && std::meta::is_public(mem)) { + constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem)); + + // Only extract this field if it's in our list of requested fields + if constexpr (should_extract(key)) { + // Try to find and extract the field + if constexpr (concepts::optional_type) { + // For optional fields, it's ok if they're missing + auto field_result = find_field_unordered(key); + if (!field_result.error()) { + auto error = field_result.get(out.[:mem:]); + if (error && error != NO_SUCH_FIELD) { + return error; + } + } else if (field_result.error() != NO_SUCH_FIELD) { + return field_result.error(); + } else { + out.[:mem:].reset(); + } + } else { + // For required fields (in the requested list), fail if missing + SIMDJSON_TRY((*this)[key].get(out.[:mem:])); + } + } + } + }; + + return SUCCESS; +} + +#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION + } // namespace ondemand } // namespace lsx } // namespace simdjson @@ -107886,7 +123841,7 @@ simdjson_inline simdjson_result simdjson_result -simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { if (error()) { return error(); } return first.get_string(receiver, allow_replacement); } @@ -107922,12 +123877,12 @@ simdjson_deprecated simdjson_inline simdjson_result simdjson_result(first).get(); } template -simdjson_inline error_code simdjson_result::get(T &out) & noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) & noexcept { if (error()) { return error(); } return first.get(out); } template -simdjson_inline error_code simdjson_result::get(T &out) && noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) && noexcept { if (error()) { return error(); } return std::forward(first).get(out); } @@ -107937,8 +123892,8 @@ template<> simdjson_deprecated simdjson_inline simdjson_result(first); } -template<> simdjson_inline error_code simdjson_result::get(lsx::ondemand::document &out) & noexcept = delete; -template<> simdjson_inline error_code simdjson_result::get(lsx::ondemand::document &out) && noexcept { +template<> simdjson_warn_unused simdjson_inline error_code simdjson_result::get(lsx::ondemand::document &out) & noexcept = delete; +template<> simdjson_warn_unused simdjson_inline error_code simdjson_result::get(lsx::ondemand::document &out) && noexcept { if (error()) { return error(); } out = std::forward(first); return SUCCESS; @@ -108056,6 +124011,15 @@ simdjson_inline simdjson_result simdjson_result + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code simdjson_result::extract_into(T& out) & noexcept { + if (error()) { return error(); } + return first.extract_into(out); +} +#endif // SIMDJSON_STATIC_REFLECTION + } // namespace simdjson @@ -108090,7 +124054,7 @@ simdjson_inline simdjson_result document_reference::get_double() noexcep simdjson_inline simdjson_result document_reference::get_double_in_string() noexcept { return doc->get_root_value_iterator().get_root_double(false); } simdjson_inline simdjson_result document_reference::get_string(bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(false, allow_replacement); } template -simdjson_inline error_code document_reference::get_string(string_type& receiver, bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(receiver, false, allow_replacement); } +simdjson_warn_unused simdjson_inline error_code document_reference::get_string(string_type& receiver, bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(receiver, false, allow_replacement); } simdjson_inline simdjson_result document_reference::get_wobbly_string() noexcept { return doc->get_root_value_iterator().get_root_wobbly_string(false); } simdjson_inline simdjson_result document_reference::get_raw_json_string() noexcept { return doc->get_root_value_iterator().get_root_raw_json_string(false); } simdjson_inline simdjson_result document_reference::get_bool() noexcept { return doc->get_root_value_iterator().get_root_bool(false); } @@ -108143,7 +124107,13 @@ simdjson_inline simdjson_result document_reference::at_pointer(std::strin simdjson_inline simdjson_result document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); } simdjson_inline simdjson_result document_reference::raw_json() noexcept { return doc->raw_json();} simdjson_inline document_reference::operator document&() const noexcept { return *doc; } - +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code document_reference::extract_into(T& out) & noexcept { + return doc->extract_into(out); +} +#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION } // namespace ondemand } // namespace lsx } // namespace simdjson @@ -108240,7 +124210,7 @@ simdjson_inline simdjson_result simdjson_result -simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { if (error()) { return error(); } return first.get_string(receiver, allow_replacement); } @@ -108275,12 +124245,12 @@ simdjson_inline simdjson_result simdjson_result(first).get(); } template -simdjson_inline error_code simdjson_result::get(T &out) & noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) & noexcept { if (error()) { return error(); } return first.get(out); } template -simdjson_inline error_code simdjson_result::get(T &out) && noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) && noexcept { if (error()) { return error(); } return std::forward(first).get(out); } @@ -108297,13 +124267,13 @@ simdjson_inline simdjson_result simdjson_result -simdjson_inline error_code simdjson_result::get(lsx::ondemand::document_reference &out) & noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(lsx::ondemand::document_reference &out) & noexcept { if (error()) { return error(); } out = first; return SUCCESS; } template <> -simdjson_inline error_code simdjson_result::get(lsx::ondemand::document_reference &out) && noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(lsx::ondemand::document_reference &out) && noexcept { if (error()) { return error(); } out = first; return SUCCESS; @@ -108391,7 +124361,14 @@ simdjson_inline simdjson_result simdjson_result + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code simdjson_result::extract_into(T& out) & noexcept { + if (error()) { return error(); } + return first.extract_into(out); +} +#endif // SIMDJSON_STATIC_REFLECTION } // namespace simdjson #endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H @@ -108582,10 +124559,19 @@ simdjson_inline document_stream::iterator& document_stream::iterator::operator++ return *this; } +simdjson_inline bool document_stream::iterator::at_end() const noexcept { + return finished; +} + + simdjson_inline bool document_stream::iterator::operator!=(const document_stream::iterator &other) const noexcept { return finished != other.finished; } +simdjson_inline bool document_stream::iterator::operator==(const document_stream::iterator &other) const noexcept { + return finished == other.finished; +} + simdjson_inline document_stream::iterator document_stream::begin() noexcept { start(); // If there are no documents, we're finished. @@ -108703,7 +124689,10 @@ inline void document_stream::next_document() noexcept { // Always set depth=1 at the start of document doc.iter._depth = 1; // consume comma if comma separated is allowed - if (allow_comma_separated) { doc.iter.consume_character(','); } + if (allow_comma_separated) { + error_code ignored = doc.iter.consume_character(','); + static_cast(ignored); // ignored on purpose + } // Resets the string buffer at the beginning, thus invalidating the strings. doc.iter._string_buf_loc = parser->string_buf.get(); doc.iter._root = doc.iter.position(); @@ -108949,7 +124938,7 @@ simdjson_inline simdjson_result simdjson_result -simdjson_inline error_code simdjson_result::unescaped_key(string_type &receiver, bool allow_replacement) noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::unescaped_key(string_type &receiver, bool allow_replacement) noexcept { if (error()) { return error(); } return first.unescaped_key(receiver, allow_replacement); } @@ -109185,6 +125174,8 @@ simdjson_inline void json_iterator::assert_valid_position(token_position positio #ifndef SIMDJSON_CLANG_VISUAL_STUDIO SIMDJSON_ASSUME( position >= &parser->implementation->structural_indexes[0] ); SIMDJSON_ASSUME( position < &parser->implementation->structural_indexes[parser->implementation->n_structural_indexes] ); +#else + (void)position; // Suppress unused parameter warning #endif } @@ -109309,7 +125300,7 @@ simdjson_inline uint8_t *&json_iterator::string_buf_loc() noexcept { return _string_buf_loc; } -simdjson_inline error_code json_iterator::report_error(error_code _error, const char *message) noexcept { +simdjson_warn_unused simdjson_inline error_code json_iterator::report_error(error_code _error, const char *message) noexcept { SIMDJSON_ASSUME(_error != SUCCESS && _error != UNINITIALIZED && _error != INCORRECT_TYPE && _error != NO_SUCH_FIELD); logger::log_error(*this, message); error = _error; @@ -109353,7 +125344,7 @@ simdjson_inline void json_iterator::reenter_child(token_position position, depth _depth = child_depth; } -simdjson_inline error_code json_iterator::consume_character(char c) noexcept { +simdjson_warn_unused simdjson_inline error_code json_iterator::consume_character(char c) noexcept { if (*peek() == c) { return_current_and_advance(); return SUCCESS; @@ -109376,7 +125367,7 @@ simdjson_inline void json_iterator::set_start_position(depth_t depth, token_posi #endif -simdjson_inline error_code json_iterator::optional_error(error_code _error, const char *message) noexcept { +simdjson_warn_unused simdjson_inline error_code json_iterator::optional_error(error_code _error, const char *message) noexcept { SIMDJSON_ASSUME(_error == INCORRECT_TYPE || _error == NO_SUCH_FIELD); logger::log_error(*this, message); return _error; @@ -109771,6 +125762,10 @@ inline void log_line(const json_iterator &iter, token_position index, depth_t de /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value-inl.h" */ +/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_string_builder.h" // for constevalutil::fixed_string */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #endif */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { @@ -109828,7 +125823,7 @@ simdjson_inline simdjson_result object::start_root(value_iterator &iter) SIMDJSON_TRY( iter.start_root_object().error() ); return object(iter); } -simdjson_inline error_code object::consume() noexcept { +simdjson_warn_unused simdjson_inline error_code object::consume() noexcept { if(iter.is_at_key()) { /** * whenever you are pointing at a key, calling skip_child() is @@ -109957,6 +125952,52 @@ simdjson_inline simdjson_result object::reset() & noexcept { return iter.reset_object(); } +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION + +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code object::extract_into(T& out) & noexcept { + // Helper to check if a field name matches any of the requested fields + auto should_extract = [](std::string_view field_name) constexpr -> bool { + return ((FieldNames.view() == field_name) || ...); + }; + + // Iterate through all members of T using reflection + template for (constexpr auto mem : std::define_static_array( + std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) { + + if constexpr (!std::meta::is_const(mem) && std::meta::is_public(mem)) { + constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem)); + + // Only extract this field if it's in our list of requested fields + if constexpr (should_extract(key)) { + // Try to find and extract the field + if constexpr (concepts::optional_type) { + // For optional fields, it's ok if they're missing + auto field_result = find_field_unordered(key); + if (!field_result.error()) { + auto error = field_result.get(out.[:mem:]); + if (error && error != NO_SUCH_FIELD) { + return error; + } + } else if (field_result.error() != NO_SUCH_FIELD) { + return field_result.error(); + } else { + out.[:mem:].reset(); + } + } else { + // For required fields (in the requested list), fail if missing + SIMDJSON_TRY((*this)[key].get(out.[:mem:])); + } + } + } + }; + + return SUCCESS; +} + +#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION + } // namespace ondemand } // namespace lsx } // namespace simdjson @@ -110033,6 +126074,7 @@ simdjson_inline simdjson_result simdjson_result parser::iterate(padded_string_view json) & noexcept { - if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; } + if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; } json.remove_utf8_bom(); @@ -110247,7 +126289,7 @@ simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(p #ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON simdjson_warn_unused simdjson_inline simdjson_result parser::iterate_allow_incomplete_json(padded_string_view json) & noexcept { - if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; } + if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; } json.remove_utf8_bom(); @@ -110279,10 +126321,7 @@ simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(s } simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(std::string &json) & noexcept { - if(json.capacity() - json.size() < SIMDJSON_PADDING) { - json.reserve(json.size() + SIMDJSON_PADDING); - } - return iterate(padded_string_view(json)); + return iterate(pad_with_reserve(json)); } simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(const std::string &json) & noexcept { @@ -110304,7 +126343,7 @@ simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(c } simdjson_warn_unused simdjson_inline simdjson_result parser::iterate_raw(padded_string_view json) & noexcept { - if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; } + if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; } json.remove_utf8_bom(); @@ -110319,6 +126358,7 @@ simdjson_warn_unused simdjson_inline simdjson_result parser::iter } inline simdjson_result parser::iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept { + // Warning: no check is done on the buffer padding. We trust the user. if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; } if((len >= 3) && (std::memcmp(buf, "\xEF\xBB\xBF", 3) == 0)) { buf += 3; @@ -110327,16 +126367,24 @@ inline simdjson_result parser::iterate_many(const uint8_t *buf, if(allow_comma_separated && batch_size < len) { batch_size = len; } return document_stream(*this, buf, len, batch_size, allow_comma_separated); } + inline simdjson_result parser::iterate_many(const char *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept { + // Warning: no check is done on the buffer padding. We trust the user. return iterate_many(reinterpret_cast(buf), len, batch_size, allow_comma_separated); } -inline simdjson_result parser::iterate_many(const std::string &s, size_t batch_size, bool allow_comma_separated) noexcept { +inline simdjson_result parser::iterate_many(padded_string_view s, size_t batch_size, bool allow_comma_separated) noexcept { + if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; } return iterate_many(s.data(), s.length(), batch_size, allow_comma_separated); } inline simdjson_result parser::iterate_many(const padded_string &s, size_t batch_size, bool allow_comma_separated) noexcept { - return iterate_many(s.data(), s.length(), batch_size, allow_comma_separated); + return iterate_many(padded_string_view(s), batch_size, allow_comma_separated); +} +inline simdjson_result parser::iterate_many(const std::string &s, size_t batch_size, bool allow_comma_separated) noexcept { + return iterate_many(padded_string_view(s), batch_size, allow_comma_separated); +} +inline simdjson_result parser::iterate_many(std::string &s, size_t batch_size, bool allow_comma_separated) noexcept { + return iterate_many(pad(s), batch_size, allow_comma_separated); } - simdjson_pure simdjson_inline size_t parser::capacity() const noexcept { return _capacity; } @@ -110371,6 +126419,34 @@ simdjson_inline simdjson_warn_unused simdjson_result parser::u return result; } +simdjson_inline simdjson_warn_unused ondemand::parser& parser::get_parser() { + return *parser::get_parser_instance(); +} + +simdjson_inline bool release_parser() { + auto &parser_instance = parser::get_threadlocal_parser_if_exists(); + if (parser_instance) { + parser_instance.reset(); + return true; + } + return false; +} + +simdjson_inline simdjson_warn_unused std::unique_ptr& parser::get_parser_instance() { + std::unique_ptr& parser_instance = get_threadlocal_parser_if_exists(); + if (!parser_instance) { + parser_instance.reset(new ondemand::parser()); + } + return parser_instance; +} + +simdjson_inline simdjson_warn_unused std::unique_ptr& parser::get_threadlocal_parser_if_exists() { + // @the-moisrex points out that this could be implemented with std::optional (C++17). + thread_local std::unique_ptr parser_instance = nullptr; + return parser_instance; +} + + } // namespace ondemand } // namespace lsx } // namespace simdjson @@ -110405,8 +126481,13 @@ namespace ondemand { simdjson_inline raw_json_string::raw_json_string(const uint8_t * _buf) noexcept : buf{_buf} {} -simdjson_inline const char * raw_json_string::raw() const noexcept { return reinterpret_cast(buf); } +simdjson_inline const char * raw_json_string::raw() const noexcept { + return reinterpret_cast(buf); +} +simdjson_inline char raw_json_string::operator[](size_t i) const noexcept { + return reinterpret_cast(buf)[i]; +} simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(std::string_view target) noexcept { size_t pos{0}; @@ -110583,6 +126664,10 @@ simdjson_inline simdjson_result simdjson_result::operator[](size_t i) const noexcept { + if (error()) { return error(); } + return first[i]; +} simdjson_inline simdjson_warn_unused simdjson_result simdjson_result::unescape(lsx::ondemand::json_iterator &iter, bool allow_replacement) const noexcept { if (error()) { return error(); } return first.unescape(iter, allow_replacement); @@ -110608,6 +126693,9 @@ simdjson_inline simdjson_warn_unused simdjson_result simdjson_ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/serialization.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */ +/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_builder.h" */ +/* amalgamation skipped (editor-only): #endif */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { @@ -110973,7 +127061,7 @@ simdjson_warn_unused simdjson_inline simdjson_result value_iterator::start if (*_json_iter->peek() == '}') { logger::log_value(*_json_iter, "empty object"); _json_iter->return_current_and_advance(); - end_container(); + SIMDJSON_TRY(end_container()); return false; } return true; @@ -111827,7 +127915,7 @@ simdjson_inline void value_iterator::advance_scalar(const char *type) noexcept { _json_iter->ascend_to(depth()-1); } -simdjson_inline error_code value_iterator::start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept { +simdjson_warn_unused simdjson_inline error_code value_iterator::start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept { logger::log_start_value(*_json_iter, start_position(), depth(), type); // If we're not at the position anymore, we don't want to advance the cursor. const uint8_t *json; @@ -111989,7 +128077,7 @@ simdjson_inline simdjson_result value_iterator::type() const noexcept case '5': case '6': case '7': case '8': case '9': return json_type::number; default: - return TAPE_ERROR; + return json_type::unknown; } } @@ -112029,6 +128117,1097 @@ simdjson_inline simdjson_result::simdjson_result( #endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H /* end file simdjson/generic/ondemand/value_iterator-inl.h for lsx */ +// JSON builder inline definitions +/* including simdjson/generic/ondemand/json_string_builder-inl.h for lsx: #include "simdjson/generic/ondemand/json_string_builder-inl.h" */ +/* begin file simdjson/generic/ondemand/json_string_builder-inl.h for lsx */ +/** + * This file is part of the builder API. It is temporarily in the ondemand + * directory but we will move it to a builder directory later. + */ +#include +#include +#include +#ifndef SIMDJSON_GENERIC_STRING_BUILDER_INL_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_INL_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_string_builder.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +/* + * Empirically, we have found that an inlined optimization is important for + * performance. The following macros are not ideal. We should find a better + * way to inline the code. + */ + +#if defined(__SSE2__) || defined(__x86_64__) || defined(__x86_64) || \ + (defined(_M_AMD64) || defined(_M_X64) || \ + (defined(_M_IX86_FP) && _M_IX86_FP == 2)) +#ifndef SIMDJSON_EXPERIMENTAL_HAS_SSE2 +#define SIMDJSON_EXPERIMENTAL_HAS_SSE2 1 +#endif +#endif + +#if defined(__aarch64__) || defined(_M_ARM64) +#ifndef SIMDJSON_EXPERIMENTAL_HAS_NEON +#define SIMDJSON_EXPERIMENTAL_HAS_NEON 1 +#endif +#endif +#if SIMDJSON_EXPERIMENTAL_HAS_NEON +#include +#endif +#if SIMDJSON_EXPERIMENTAL_HAS_SSE2 +#include +#endif + +namespace simdjson { +namespace lsx { +namespace builder { + +static SIMDJSON_CONSTEXPR_LAMBDA std::array + json_quotable_character = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 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, 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, 1, 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, 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, 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, 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, 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, 0, 0, 0}; + +/** + +A possible SWAR implementation of has_json_escapable_byte. It is not used +because it is slower than the current implementation. It is kept here for +reference (to show that we tried it). + +inline bool has_json_escapable_byte(uint64_t x) { + uint64_t is_ascii = 0x8080808080808080ULL & ~x; + uint64_t xor2 = x ^ 0x0202020202020202ULL; + uint64_t lt32_or_eq34 = xor2 - 0x2121212121212121ULL; + uint64_t sub92 = x ^ 0x5C5C5C5C5C5C5C5CULL; + uint64_t eq92 = (sub92 - 0x0101010101010101ULL); + return ((lt32_or_eq34 | eq92) & is_ascii) != 0; +} + +**/ + +SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline bool +simple_needs_escaping(std::string_view v) { + for (char c : v) { + // a table lookup is faster than a series of comparisons + if (json_quotable_character[static_cast(c)]) { + return true; + } + } + return false; +} + +#if SIMDJSON_EXPERIMENTAL_HAS_NEON +simdjson_inline bool fast_needs_escaping(std::string_view view) { + if (view.size() < 16) { + return simple_needs_escaping(view); + } + size_t i = 0; + uint8x16_t running = vdupq_n_u8(0); + uint8x16_t v34 = vdupq_n_u8(34); + uint8x16_t v92 = vdupq_n_u8(92); + + for (; i + 15 < view.size(); i += 16) { + uint8x16_t word = vld1q_u8((const uint8_t *)view.data() + i); + running = vorrq_u8(running, vceqq_u8(word, v34)); + running = vorrq_u8(running, vceqq_u8(word, v92)); + running = vorrq_u8(running, vcltq_u8(word, vdupq_n_u8(32))); + } + if (i < view.size()) { + uint8x16_t word = + vld1q_u8((const uint8_t *)view.data() + view.length() - 16); + running = vorrq_u8(running, vceqq_u8(word, v34)); + running = vorrq_u8(running, vceqq_u8(word, v92)); + running = vorrq_u8(running, vcltq_u8(word, vdupq_n_u8(32))); + } + return vmaxvq_u32(vreinterpretq_u32_u8(running)) != 0; +} +#elif SIMDJSON_EXPERIMENTAL_HAS_SSE2 +simdjson_inline bool fast_needs_escaping(std::string_view view) { + if (view.size() < 16) { + return simple_needs_escaping(view); + } + size_t i = 0; + __m128i running = _mm_setzero_si128(); + for (; i + 15 < view.size(); i += 16) { + + __m128i word = + _mm_loadu_si128(reinterpret_cast(view.data() + i)); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(34))); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(92))); + running = _mm_or_si128( + running, _mm_cmpeq_epi8(_mm_subs_epu8(word, _mm_set1_epi8(31)), + _mm_setzero_si128())); + } + if (i < view.size()) { + __m128i word = _mm_loadu_si128( + reinterpret_cast(view.data() + view.length() - 16)); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(34))); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(92))); + running = _mm_or_si128( + running, _mm_cmpeq_epi8(_mm_subs_epu8(word, _mm_set1_epi8(31)), + _mm_setzero_si128())); + } + return _mm_movemask_epi8(running) != 0; +} +#else +simdjson_inline bool fast_needs_escaping(std::string_view view) { + return simple_needs_escaping(view); +} +#endif + +SIMDJSON_CONSTEXPR_LAMBDA inline size_t +find_next_json_quotable_character(const std::string_view view, + size_t location) noexcept { + + for (auto pos = view.begin() + location; pos != view.end(); ++pos) { + if (json_quotable_character[static_cast(*pos)]) { + return pos - view.begin(); + } + } + return size_t(view.size()); +} + +SIMDJSON_CONSTEXPR_LAMBDA static std::string_view control_chars[] = { + "\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", "\\u0006", + "\\u0007", "\\b", "\\t", "\\n", "\\u000b", "\\f", "\\r", + "\\u000e", "\\u000f", "\\u0010", "\\u0011", "\\u0012", "\\u0013", "\\u0014", + "\\u0015", "\\u0016", "\\u0017", "\\u0018", "\\u0019", "\\u001a", "\\u001b", + "\\u001c", "\\u001d", "\\u001e", "\\u001f"}; + +// All Unicode characters may be placed within the quotation marks, except for +// the characters that MUST be escaped: quotation mark, reverse solidus, and the +// control characters (U+0000 through U+001F). There are two-character sequence +// escape representations of some popular characters: +// \", \\, \b, \f, \n, \r, \t. +SIMDJSON_CONSTEXPR_LAMBDA void escape_json_char(char c, char *&out) { + if (c == '"') { + memcpy(out, "\\\"", 2); + out += 2; + } else if (c == '\\') { + memcpy(out, "\\\\", 2); + out += 2; + } else { + std::string_view v = control_chars[uint8_t(c)]; + memcpy(out, v.data(), v.size()); + out += v.size(); + } +} + +inline size_t write_string_escaped(const std::string_view input, char *out) { + size_t mysize = input.size(); + if (!fast_needs_escaping(input)) { // fast path! + memcpy(out, input.data(), input.size()); + return input.size(); + } + const char *const initout = out; + size_t location = find_next_json_quotable_character(input, 0); + memcpy(out, input.data(), location); + out += location; + escape_json_char(input[location], out); + location += 1; + while (location < mysize) { + size_t newlocation = find_next_json_quotable_character(input, location); + memcpy(out, input.data() + location, newlocation - location); + out += newlocation - location; + location = newlocation; + if (location == mysize) { + break; + } + escape_json_char(input[location], out); + location += 1; + } + return out - initout; +} + +simdjson_inline string_builder::string_builder(size_t initial_capacity) + : buffer(new(std::nothrow) char[initial_capacity]), position(0), + capacity(buffer.get() != nullptr ? initial_capacity : 0), + is_valid(buffer.get() != nullptr) {} + +simdjson_inline bool string_builder::capacity_check(size_t upcoming_bytes) { + // We use the convention that when is_valid is false, then the capacity and + // the position are 0. + // Most of the time, this function will return true. + if (simdjson_likely(upcoming_bytes <= capacity - position)) { + return true; + } + // check for overflow, most of the time there is no overflow + if (simdjson_likely(position + upcoming_bytes < position)) { + return false; + } + // We will rarely get here. + grow_buffer((std::max)(capacity * 2, position + upcoming_bytes)); + // If the buffer allocation failed, we set is_valid to false. + return is_valid; +} + +simdjson_inline void string_builder::grow_buffer(size_t desired_capacity) { + if (!is_valid) { + return; + } + std::unique_ptr new_buffer(new (std::nothrow) char[desired_capacity]); + if (new_buffer.get() == nullptr) { + set_valid(false); + return; + } + std::memcpy(new_buffer.get(), buffer.get(), position); + buffer.swap(new_buffer); + capacity = desired_capacity; +} + +simdjson_inline void string_builder::set_valid(bool valid) noexcept { + if (!valid) { + is_valid = false; + capacity = 0; + position = 0; + buffer.reset(); + } else { + is_valid = true; + } +} + +simdjson_inline size_t string_builder::size() const noexcept { + return position; +} + +simdjson_inline void string_builder::append(char c) noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = c; + } +} + +simdjson_inline void string_builder::append_null() noexcept { + constexpr char null_literal[] = "null"; + constexpr size_t null_len = sizeof(null_literal) - 1; + if (capacity_check(null_len)) { + std::memcpy(buffer.get() + position, null_literal, null_len); + position += null_len; + } +} + +simdjson_inline void string_builder::clear() noexcept { + position = 0; + // if it was invalid, we should try to repair it + if (!is_valid) { + capacity = 0; + buffer.reset(); + is_valid = true; + } +} + +namespace internal { + +template ::value>::type> +simdjson_really_inline int int_log2(number_type x) { + return 63 - leading_zeroes(uint64_t(x) | 1); +} + +simdjson_really_inline int fast_digit_count_32(uint32_t x) { + static uint64_t table[] = { + 4294967296, 8589934582, 8589934582, 8589934582, 12884901788, + 12884901788, 12884901788, 17179868184, 17179868184, 17179868184, + 21474826480, 21474826480, 21474826480, 21474826480, 25769703776, + 25769703776, 25769703776, 30063771072, 30063771072, 30063771072, + 34349738368, 34349738368, 34349738368, 34349738368, 38554705664, + 38554705664, 38554705664, 41949672960, 41949672960, 41949672960, + 42949672960, 42949672960}; + return uint32_t((x + table[int_log2(x)]) >> 32); +} + +simdjson_really_inline int fast_digit_count_64(uint64_t x) { + static uint64_t table[] = {9, + 99, + 999, + 9999, + 99999, + 999999, + 9999999, + 99999999, + 999999999, + 9999999999, + 99999999999, + 999999999999, + 9999999999999, + 99999999999999, + 999999999999999ULL, + 9999999999999999ULL, + 99999999999999999ULL, + 999999999999999999ULL, + 9999999999999999999ULL}; + int y = (19 * int_log2(x) >> 6); + y += x > table[y]; + return y + 1; +} + +template ::value>::type> +simdjson_really_inline size_t digit_count(number_type v) noexcept { + static_assert(sizeof(number_type) == 8 || sizeof(number_type) == 4 || + sizeof(number_type) == 2 || sizeof(number_type) == 1, + "We only support 8-bit, 16-bit, 32-bit and 64-bit numbers"); + SIMDJSON_IF_CONSTEXPR(sizeof(number_type) <= 4) { + return fast_digit_count_32(static_cast(v)); + } + else { + return fast_digit_count_64(static_cast(v)); + } +} +static const char decimal_table[200] = { + 0x30, 0x30, 0x30, 0x31, 0x30, 0x32, 0x30, 0x33, 0x30, 0x34, 0x30, 0x35, + 0x30, 0x36, 0x30, 0x37, 0x30, 0x38, 0x30, 0x39, 0x31, 0x30, 0x31, 0x31, + 0x31, 0x32, 0x31, 0x33, 0x31, 0x34, 0x31, 0x35, 0x31, 0x36, 0x31, 0x37, + 0x31, 0x38, 0x31, 0x39, 0x32, 0x30, 0x32, 0x31, 0x32, 0x32, 0x32, 0x33, + 0x32, 0x34, 0x32, 0x35, 0x32, 0x36, 0x32, 0x37, 0x32, 0x38, 0x32, 0x39, + 0x33, 0x30, 0x33, 0x31, 0x33, 0x32, 0x33, 0x33, 0x33, 0x34, 0x33, 0x35, + 0x33, 0x36, 0x33, 0x37, 0x33, 0x38, 0x33, 0x39, 0x34, 0x30, 0x34, 0x31, + 0x34, 0x32, 0x34, 0x33, 0x34, 0x34, 0x34, 0x35, 0x34, 0x36, 0x34, 0x37, + 0x34, 0x38, 0x34, 0x39, 0x35, 0x30, 0x35, 0x31, 0x35, 0x32, 0x35, 0x33, + 0x35, 0x34, 0x35, 0x35, 0x35, 0x36, 0x35, 0x37, 0x35, 0x38, 0x35, 0x39, + 0x36, 0x30, 0x36, 0x31, 0x36, 0x32, 0x36, 0x33, 0x36, 0x34, 0x36, 0x35, + 0x36, 0x36, 0x36, 0x37, 0x36, 0x38, 0x36, 0x39, 0x37, 0x30, 0x37, 0x31, + 0x37, 0x32, 0x37, 0x33, 0x37, 0x34, 0x37, 0x35, 0x37, 0x36, 0x37, 0x37, + 0x37, 0x38, 0x37, 0x39, 0x38, 0x30, 0x38, 0x31, 0x38, 0x32, 0x38, 0x33, + 0x38, 0x34, 0x38, 0x35, 0x38, 0x36, 0x38, 0x37, 0x38, 0x38, 0x38, 0x39, + 0x39, 0x30, 0x39, 0x31, 0x39, 0x32, 0x39, 0x33, 0x39, 0x34, 0x39, 0x35, + 0x39, 0x36, 0x39, 0x37, 0x39, 0x38, 0x39, 0x39, +}; +} // namespace internal + +template +simdjson_inline void string_builder::append(number_type v) noexcept { + static_assert(std::is_same::value || + std::is_integral::value || + std::is_floating_point::value, + "Unsupported number type"); + // If C++17 is available, we can 'if constexpr' here. + SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + if (v) { + constexpr char true_literal[] = "true"; + constexpr size_t true_len = sizeof(true_literal) - 1; + if (capacity_check(true_len)) { + std::memcpy(buffer.get() + position, true_literal, true_len); + position += true_len; + } + } else { + constexpr char false_literal[] = "false"; + constexpr size_t false_len = sizeof(false_literal) - 1; + if (capacity_check(false_len)) { + std::memcpy(buffer.get() + position, false_literal, false_len); + position += false_len; + } + } + } + else SIMDJSON_IF_CONSTEXPR(std::is_unsigned::value) { + constexpr size_t max_number_size = 20; + if (capacity_check(max_number_size)) { + using unsigned_type = typename std::make_unsigned::type; + unsigned_type pv = static_cast(v); + size_t dc = internal::digit_count(pv); + char *write_pointer = buffer.get() + position + dc - 1; + while (pv >= 100) { + memcpy(write_pointer - 1, &internal::decimal_table[(pv % 100) * 2], 2); + write_pointer -= 2; + pv /= 100; + } + if (pv >= 10) { + *write_pointer-- = char('0' + (pv % 10)); + pv /= 10; + } + *write_pointer = char('0' + pv); + position += dc; + } + } + else SIMDJSON_IF_CONSTEXPR(std::is_integral::value) { + constexpr size_t max_number_size = 20; + if (capacity_check(max_number_size)) { + using unsigned_type = typename std::make_unsigned::type; + bool negative = v < 0; + unsigned_type pv = static_cast(v); + if (negative) { + pv = 0 - pv; // the 0 is for Microsoft + } + size_t dc = internal::digit_count(pv); + if (negative) { + buffer.get()[position++] = '-'; + } + char *write_pointer = buffer.get() + position + dc - 1; + while (pv >= 100) { + memcpy(write_pointer - 1, &internal::decimal_table[(pv % 100) * 2], 2); + write_pointer -= 2; + pv /= 100; + } + if (pv >= 10) { + *write_pointer-- = char('0' + (pv % 10)); + pv /= 10; + } + *write_pointer = char('0' + pv); + position += dc; + } + } + else SIMDJSON_IF_CONSTEXPR(std::is_floating_point::value) { + constexpr size_t max_number_size = 24; + if (capacity_check(max_number_size)) { + // We could specialize for float. + char *end = simdjson::internal::to_chars(buffer.get() + position, nullptr, + double(v)); + position = end - buffer.get(); + } + } +} + +simdjson_inline void +string_builder::escape_and_append(std::string_view input) noexcept { + // escaping might turn a control character into \x00xx so 6 characters. + if (capacity_check(6 * input.size())) { + position += write_string_escaped(input, buffer.get() + position); + } +} + +simdjson_inline void +string_builder::escape_and_append_with_quotes(std::string_view input) noexcept { + // escaping might turn a control character into \x00xx so 6 characters. + if (capacity_check(2 + 6 * input.size())) { + buffer.get()[position++] = '"'; + position += write_string_escaped(input, buffer.get() + position); + buffer.get()[position++] = '"'; + } +} + +simdjson_inline void +string_builder::escape_and_append_with_quotes(char input) noexcept { + // escaping might turn a control character into \x00xx so 6 characters. + if (capacity_check(2 + 6 * 1)) { + buffer.get()[position++] = '"'; + std::string_view cinput(&input, 1); + position += write_string_escaped(cinput, buffer.get() + position); + buffer.get()[position++] = '"'; + } +} + +simdjson_inline void +string_builder::escape_and_append_with_quotes(const char *input) noexcept { + std::string_view cinput(input); + escape_and_append_with_quotes(cinput); +} +#if SIMDJSON_SUPPORTS_CONCEPTS +template +simdjson_inline void string_builder::escape_and_append_with_quotes() noexcept { + escape_and_append_with_quotes(constevalutil::string_constant::value); +} +#endif + +simdjson_inline void string_builder::append_raw(const char *c) noexcept { + size_t len = std::strlen(c); + append_raw(c, len); +} + +simdjson_inline void +string_builder::append_raw(std::string_view input) noexcept { + if (capacity_check(input.size())) { + std::memcpy(buffer.get() + position, input.data(), input.size()); + position += input.size(); + } +} + +simdjson_inline void string_builder::append_raw(const char *str, + size_t len) noexcept { + if (capacity_check(len)) { + std::memcpy(buffer.get() + position, str, len); + position += len; + } +} +#if SIMDJSON_SUPPORTS_CONCEPTS +// Support for optional types (std::optional, etc.) +template + requires(!require_custom_serialization) +simdjson_inline void string_builder::append(const T &opt) { + if (opt) { + append(*opt); + } else { + append_null(); + } +} + +template + requires(require_custom_serialization) +simdjson_inline void string_builder::append(const T &val) { + serialize(*this, val); +} + +template + requires(std::is_convertible::value || + std::is_same::value) +simdjson_inline void string_builder::append(const T &value) { + escape_and_append_with_quotes(value); +} +#endif + +#if SIMDJSON_SUPPORTS_RANGES && SIMDJSON_SUPPORTS_CONCEPTS +// Support for range-based appending (std::ranges::view, etc.) +template + requires(!std::is_convertible::value) +simdjson_inline void string_builder::append(const R &range) noexcept { + auto it = std::ranges::begin(range); + auto end = std::ranges::end(range); + if constexpr (concepts::is_pair) { + start_object(); + + if (it == end) { + end_object(); + return; // Handle empty range + } + // Append first item without leading comma + append_key_value(it->first, it->second); + ++it; + + // Append remaining items with preceding commas + for (; it != end; ++it) { + append_comma(); + append_key_value(it->first, it->second); + } + end_object(); + } else { + start_array(); + if (it == end) { + end_array(); + return; // Handle empty range + } + + // Append first item without leading comma + append(*it); + ++it; + + // Append remaining items with preceding commas + for (; it != end; ++it) { + append_comma(); + append(*it); + } + end_array(); + } +} + +#endif + +#if SIMDJSON_EXCEPTIONS +simdjson_inline string_builder::operator std::string() const noexcept(false) { + return std::string(operator std::string_view()); +} + +simdjson_inline string_builder::operator std::string_view() const + noexcept(false) simdjson_lifetime_bound { + return view(); +} +#endif + +simdjson_inline simdjson_result +string_builder::view() const noexcept { + if (!is_valid) { + return simdjson::OUT_OF_CAPACITY; + } + return std::string_view(buffer.get(), position); +} + +simdjson_inline simdjson_result string_builder::c_str() noexcept { + if (capacity_check(1)) { + buffer.get()[position] = '\0'; + return buffer.get(); + } + return simdjson::OUT_OF_CAPACITY; +} + +simdjson_inline bool string_builder::validate_unicode() const noexcept { + return simdjson::validate_utf8(buffer.get(), position); +} + +simdjson_inline void string_builder::start_object() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = '{'; + } +} + +simdjson_inline void string_builder::end_object() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = '}'; + } +} + +simdjson_inline void string_builder::start_array() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = '['; + } +} + +simdjson_inline void string_builder::end_array() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = ']'; + } +} + +simdjson_inline void string_builder::append_comma() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = ','; + } +} + +simdjson_inline void string_builder::append_colon() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = ':'; + } +} + +template +simdjson_inline void +string_builder::append_key_value(key_type key, value_type value) noexcept { + static_assert(std::is_same::value || + std::is_convertible::value, + "Unsupported key type"); + escape_and_append_with_quotes(key); + append_colon(); + SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + append_null(); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR( + std::is_convertible::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else { + append(value); + } +} + +#if SIMDJSON_SUPPORTS_CONCEPTS +template +simdjson_inline void +string_builder::append_key_value(value_type value) noexcept { + escape_and_append_with_quotes(); + append_colon(); + SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + append_null(); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR( + std::is_convertible::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else { + append(value); + } +} +#endif + +} // namespace builder +} // namespace lsx +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_STRING_BUILDER_INL_H +/* end file simdjson/generic/ondemand/json_string_builder-inl.h for lsx */ +/* including simdjson/generic/ondemand/json_builder.h for lsx: #include "simdjson/generic/ondemand/json_builder.h" */ +/* begin file simdjson/generic/ondemand/json_builder.h for lsx */ +/** + * This file is part of the builder API. It is temporarily in the ondemand directory + * but we will move it to a builder directory later. + */ +#ifndef SIMDJSON_GENERIC_BUILDER_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_string_builder.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/concepts.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ +#if SIMDJSON_STATIC_REFLECTION + +#include +#include +#include +#include +#include +#include +#include +#include +// #include // for std::define_static_string - header not available yet + +namespace simdjson { +namespace lsx { +namespace builder { + +template + requires(concepts::container_but_not_string && !require_custom_serialization) +constexpr void atom(string_builder &b, const T &t) { + auto it = t.begin(); + auto end = t.end(); + if (it == end) { + b.append_raw("[]"); + return; + } + b.append('['); + atom(b, *it); + ++it; + for (; it != end; ++it) { + b.append(','); + atom(b, *it); + } + b.append(']'); +} + +template + requires(std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v) +constexpr void atom(string_builder &b, const T &t) { + b.escape_and_append_with_quotes(t); +} + +template + requires(!require_custom_serialization) +constexpr void atom(string_builder &b, const T &m) { + if (m.empty()) { + b.append_raw("{}"); + return; + } + b.append('{'); + bool first = true; + for (const auto& [key, value] : m) { + if (!first) { + b.append(','); + } + first = false; + // Keys must be convertible to string_view per the concept + b.escape_and_append_with_quotes(key); + b.append(':'); + atom(b, value); + } + b.append('}'); +} + + +template::value && !std::is_same_v>::type> +constexpr void atom(string_builder &b, const number_type t) { + b.append(t); +} + +template + requires(std::is_class_v && !concepts::container_but_not_string && + !concepts::string_view_keyed_map && + !concepts::optional_type && + !concepts::smart_pointer && + !concepts::appendable_containers && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && !require_custom_serialization) +constexpr void atom(string_builder &b, const T &t) { + int i = 0; + b.append('{'); + template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) { + if (i != 0) + b.append(','); + constexpr auto key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(dm))); + b.append_raw(key); + b.append(':'); + atom(b, t.[:dm:]); + i++; + }; + b.append('}'); +} + +// Support for optional types (std::optional, etc.) +template + requires(!require_custom_serialization) +constexpr void atom(string_builder &b, const T &opt) { + if (opt) { + atom(b, opt.value()); + } else { + b.append_raw("null"); + } +} + +// Support for smart pointers (std::unique_ptr, std::shared_ptr, etc.) +template + requires(!require_custom_serialization) +constexpr void atom(string_builder &b, const T &ptr) { + if (ptr) { + atom(b, *ptr); + } else { + b.append_raw("null"); + } +} + +// Support for enums - serialize as string representation using expand approach from P2996R12 +template + requires(std::is_enum_v && !require_custom_serialization) +void atom(string_builder &b, const T &e) { +#if SIMDJSON_STATIC_REFLECTION + constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T)); + template for (constexpr auto enum_val : enumerators) { + constexpr auto enum_str = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(enum_val))); + if (e == [:enum_val:]) { + b.append_raw(enum_str); + return; + } + }; + // Fallback to integer if enum value not found + atom(b, static_cast>(e)); +#else + // Fallback: serialize as integer if reflection not available + atom(b, static_cast>(e)); +#endif +} + +// Support for appendable containers that don't have operator[] (sets, etc.) +template + requires(!concepts::container_but_not_string && !concepts::string_view_keyed_map && + !concepts::optional_type && !concepts::smart_pointer && + !std::is_same_v && + !std::is_same_v && !std::is_same_v && !require_custom_serialization) +constexpr void atom(string_builder &b, const T &container) { + if (container.empty()) { + b.append_raw("[]"); + return; + } + b.append('['); + bool first = true; + for (const auto& item : container) { + if (!first) { + b.append(','); + } + first = false; + atom(b, item); + } + b.append(']'); +} + +// append functions that delegate to atom functions for primitive types +template + requires(std::is_arithmetic_v && !std::is_same_v) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!concepts::container_but_not_string && !concepts::string_view_keyed_map && + !concepts::optional_type && !concepts::smart_pointer && + !std::is_same_v && + !std::is_same_v && !std::is_same_v && !require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +// works for struct +template + requires(std::is_class_v && !concepts::container_but_not_string && + !concepts::string_view_keyed_map && + !concepts::optional_type && + !concepts::smart_pointer && + !concepts::appendable_containers && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && !require_custom_serialization) +void append(string_builder &b, const Z &z) { + int i = 0; + b.append('{'); + template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^Z, std::meta::access_context::unchecked()))) { + if (i != 0) + b.append(','); + constexpr auto key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(dm))); + b.append_raw(key); + b.append(':'); + atom(b, z.[:dm:]); + i++; + }; + b.append('}'); +} + +// works for container that have begin() and end() iterators +template + requires(concepts::container_but_not_string && !require_custom_serialization) +void append(string_builder &b, const Z &z) { + auto it = z.begin(); + auto end = z.end(); + if (it == end) { + b.append_raw("[]"); + return; + } + b.append('['); + atom(b, *it); + ++it; + for (; it != end; ++it) { + b.append(','); + atom(b, *it); + } + b.append(']'); +} + +template + requires (require_custom_serialization) +void append(string_builder &b, const Z &z) { + b.append(z); +} + + +template +simdjson_warn_unused simdjson_result to_json_string(const Z &z, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) { + string_builder b(initial_capacity); + append(b, z); + std::string_view s; + if(auto e = b.view().get(s); e) { return e; } + return std::string(s); +} + +template +simdjson_warn_unused simdjson_error to_json(const Z &z, std::string &s, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) { + string_builder b(initial_capacity); + append(b, z); + std::string_view view; + if(auto e = b.view().get(view); e) { return e; } + s.assign(view); + return SUCCESS; +} + +template +string_builder& operator<<(string_builder& b, const Z& z) { + append(b, z); + return b; +} + +// extract_from: Serialize only specific fields from a struct to JSON +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +void extract_from(string_builder &b, const T &obj) { + // Helper to check if a field name matches any of the requested fields + auto should_extract = [](std::string_view field_name) constexpr -> bool { + return ((FieldNames.view() == field_name) || ...); + }; + + b.append('{'); + bool first = true; + + // Iterate through all members of T using reflection + template for (constexpr auto mem : std::define_static_array( + std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) { + + if constexpr (std::meta::is_public(mem)) { + constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem)); + + // Only serialize this field if it's in our list of requested fields + if constexpr (should_extract(key)) { + if (!first) { + b.append(','); + } + first = false; + + // Serialize the key + constexpr auto quoted_key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(mem))); + b.append_raw(quoted_key); + b.append(':'); + + // Serialize the value + atom(b, obj.[:mem:]); + } + } + }; + + b.append('}'); +} + +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_result extract_from(const T &obj, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) { + string_builder b(initial_capacity); + extract_from(b, obj); + std::string_view s; + if(auto e = b.view().get(s); e) { return e; } + return std::string(s); +} + +} // namespace builder +} // namespace lsx +// Alias the function template to 'to' in the global namespace +template +simdjson_warn_unused simdjson_result to_json(const Z &z, size_t initial_capacity = lsx::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { + lsx::builder::string_builder b(initial_capacity); + lsx::builder::append(b, z); + std::string_view s; + if(auto e = b.view().get(s); e) { return e; } + return std::string(s); +} +template +simdjson_warn_unused simdjson_error to_json(const Z &z, std::string &s, size_t initial_capacity = lsx::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { + lsx::builder::string_builder b(initial_capacity); + lsx::builder::append(b, z); + std::string_view view; + if(auto e = b.view().get(view); e) { return e; } + s.assign(view); + return SUCCESS; +} +// Global namespace function for extract_from +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_result extract_from(const T &obj, size_t initial_capacity = lsx::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { + lsx::builder::string_builder b(initial_capacity); + lsx::builder::extract_from(b, obj); + std::string_view s; + if(auto e = b.view().get(s); e) { return e; } + return std::string(s); +} + +} // namespace simdjson + +#endif // SIMDJSON_STATIC_REFLECTION + +#endif +/* end file simdjson/generic/ondemand/json_builder.h for lsx */ /* end file simdjson/generic/ondemand/amalgamated.h for lsx */ /* including simdjson/lsx/end.h: #include "simdjson/lsx/end.h" */ @@ -112637,7 +129816,7 @@ using namespace simd; struct backslash_and_quote { public: static constexpr uint32_t BYTES_PROCESSED = 32; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); + simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; } simdjson_inline bool has_backslash() { return bs_bits != 0; } @@ -112660,6 +129839,31 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin }; } + +struct escaping { + static constexpr uint32_t BYTES_PROCESSED = 16; + simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst); + + simdjson_inline bool has_escape() { return escape_bits != 0; } + simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); } + + uint64_t escape_bits; +}; // struct escaping + + + +simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) { + static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes"); + simd8 v(src); + v.store(dst); + simd8 is_quote = (v == '"'); + simd8 is_backslash = (v == '\\'); + simd8 is_control = (v < 32); + return { + (is_backslash | is_quote | is_control).to_bitmask() + }; +} + } // unnamed namespace } // namespace lasx } // namespace simdjson @@ -112728,7 +129932,7 @@ class value_iterator; /* end file simdjson/generic/ondemand/base.h for lasx */ /* including simdjson/generic/ondemand/deserialize.h for lasx: #include "simdjson/generic/ondemand/deserialize.h" */ /* begin file simdjson/generic/ondemand/deserialize.h for lasx */ -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS #ifndef SIMDJSON_ONDEMAND_DESERIALIZE_H /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ @@ -112737,55 +129941,8 @@ class value_iterator; /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ -#include namespace simdjson { -namespace tag_invoke_fn_ns { -void tag_invoke(); - -struct tag_invoke_fn { - template - requires requires(Tag tag, Args &&...args) { - tag_invoke(std::forward(tag), std::forward(args)...); - } - constexpr auto operator()(Tag tag, Args &&...args) const - noexcept(noexcept(tag_invoke(std::forward(tag), - std::forward(args)...))) - -> decltype(tag_invoke(std::forward(tag), - std::forward(args)...)) { - return tag_invoke(std::forward(tag), std::forward(args)...); - } -}; -} // namespace tag_invoke_fn_ns - -inline namespace tag_invoke_ns { -inline constexpr tag_invoke_fn_ns::tag_invoke_fn tag_invoke = {}; -} // namespace tag_invoke_ns - -template -concept tag_invocable = requires(Tag tag, Args... args) { - tag_invoke(std::forward(tag), std::forward(args)...); -}; - -template -concept nothrow_tag_invocable = - tag_invocable && requires(Tag tag, Args... args) { - { - tag_invoke(std::forward(tag), std::forward(args)...) - } noexcept; - }; - -template -using tag_invoke_result = - std::invoke_result; - -template -using tag_invoke_result_t = - std::invoke_result_t; - -template using tag_t = std::decay_t; - - struct deserialize_tag; /// These types are deserializable in a built-in way @@ -112807,7 +129964,7 @@ template concept custom_deserializable = tag_invocable; template -concept deserializable = custom_deserializable || is_builtin_deserializable_v; +concept deserializable = custom_deserializable || is_builtin_deserializable_v || concepts::optional_type; template concept nothrow_custom_deserializable = nothrow_tag_invocable; @@ -112818,28 +129975,44 @@ concept nothrow_deserializable = nothrow_custom_deserializable || is_bu /// Deserialize Tag inline constexpr struct deserialize_tag { + using array_type = lasx::ondemand::array; + using object_type = lasx::ondemand::object; using value_type = lasx::ondemand::value; using document_type = lasx::ondemand::document; using document_reference_type = lasx::ondemand::document_reference; + // Customization Point for array + template + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(array_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for object + template + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(object_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + // Customization Point for value template requires custom_deserializable - [[nodiscard]] constexpr /* error_code */ auto operator()(value_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + simdjson_warn_unused constexpr /* error_code */ auto operator()(value_type &object, T& output) const noexcept(nothrow_custom_deserializable) { return tag_invoke(*this, object, output); } // Customization Point for document template requires custom_deserializable - [[nodiscard]] constexpr /* error_code */ auto operator()(document_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + simdjson_warn_unused constexpr /* error_code */ auto operator()(document_type &object, T& output) const noexcept(nothrow_custom_deserializable) { return tag_invoke(*this, object, output); } // Customization Point for document reference template requires custom_deserializable - [[nodiscard]] constexpr /* error_code */ auto operator()(document_reference_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + simdjson_warn_unused constexpr /* error_code */ auto operator()(document_reference_type &object, T& output) const noexcept(nothrow_custom_deserializable) { return tag_invoke(*this, object, output); } @@ -112849,7 +130022,7 @@ inline constexpr struct deserialize_tag { } // namespace simdjson #endif // SIMDJSON_ONDEMAND_DESERIALIZE_H -#endif // SIMDJSON_SUPPORTS_DESERIALIZATION +#endif // SIMDJSON_SUPPORTS_CONCEPTS /* end file simdjson/generic/ondemand/deserialize.h for lasx */ /* including simdjson/generic/ondemand/value_iterator.h for lasx: #include "simdjson/generic/ondemand/value_iterator.h" */ @@ -113191,7 +130364,7 @@ class value_iterator { simdjson_warn_unused simdjson_inline simdjson_result get_root_number(bool check_trailing) noexcept; simdjson_warn_unused simdjson_inline simdjson_result is_root_null(bool check_trailing) noexcept; - simdjson_inline error_code error() const noexcept; + simdjson_warn_unused simdjson_inline error_code error() const noexcept; simdjson_inline uint8_t *&string_buf_loc() noexcept; simdjson_inline const json_iterator &json_iter() const noexcept; simdjson_inline json_iterator &json_iter() noexcept; @@ -113275,8 +130448,8 @@ class value_iterator { simdjson_inline const uint8_t *peek_non_root_scalar(const char *type) noexcept; - simdjson_inline error_code start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept; - simdjson_inline error_code end_container() noexcept; + simdjson_warn_unused simdjson_inline error_code start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept; + simdjson_warn_unused simdjson_inline error_code end_container() noexcept; /** * Advance to a place expecting a value (increasing depth). @@ -113286,8 +130459,8 @@ class value_iterator { */ simdjson_inline simdjson_result advance_to_value() noexcept; - simdjson_inline error_code incorrect_type_error(const char *message) const noexcept; - simdjson_inline error_code error_unless_more_tokens(uint32_t tokens=1) const noexcept; + simdjson_warn_unused simdjson_inline error_code incorrect_type_error(const char *message) const noexcept; + simdjson_warn_unused simdjson_inline error_code error_unless_more_tokens(uint32_t tokens=1) const noexcept; simdjson_inline bool is_at_start() const noexcept; /** @@ -113324,7 +130497,7 @@ class value_iterator { /** @copydoc error_code json_iterator::end_position() const noexcept; */ simdjson_inline token_position end_position() const noexcept; /** @copydoc error_code json_iterator::report_error(error_code error, const char *message) noexcept; */ - simdjson_inline error_code report_error(error_code error, const char *message) noexcept; + simdjson_warn_unused simdjson_inline error_code report_error(error_code error, const char *message) noexcept; friend class document; friend class object; @@ -113389,13 +130562,14 @@ class value { * * You may use get_double(), get_bool(), get_uint64(), get_int64(), * get_object(), get_array(), get_raw_json_string(), or get_string() instead. + * When SIMDJSON_SUPPORTS_CONCEPTS is set, custom types are also supported. * * @returns A value of the given type, parsed from the JSON. * @returns INCORRECT_TYPE If the JSON value is not the given type. */ template simdjson_inline simdjson_result get() -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept @@ -113412,22 +130586,38 @@ class value { * Get this value as the given type. * * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool + * If the macro SIMDJSON_SUPPORTS_CONCEPTS is set, then custom types are also supported. * * @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized. * @returns INCORRECT_TYPE If the JSON value is not an object. * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. */ template - simdjson_inline error_code get(T &out) -#if SIMDJSON_SUPPORTS_DESERIALIZATION + simdjson_warn_unused simdjson_inline error_code get(T &out) +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept #endif { - #if SIMDJSON_SUPPORTS_DESERIALIZATION + #if SIMDJSON_SUPPORTS_CONCEPTS if constexpr (custom_deserializable) { return deserialize(*this, out); + } else if constexpr (concepts::optional_type) { + using value_type = typename std::remove_cvref_t::value_type; + + // Check if the value is null + bool is_null_value; + SIMDJSON_TRY( is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); // Set to nullopt + return SUCCESS; + } + + if (!out) { + out.emplace(); + } + return get(out.value()); } else { static_assert(!sizeof(T), "The get method with type T is not implemented by the simdjson library. " "And you do not seem to have added support for it. Indeed, we have that " @@ -113437,7 +130627,7 @@ class value { static_cast(out); // to get rid of unused errors return UNINITIALIZED; } -#else // SIMDJSON_SUPPORTS_DESERIALIZATION +#else // SIMDJSON_SUPPORTS_CONCEPTS // Unless the simdjson library or the user provides an inline implementation, calling this method should // immediately fail. static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " @@ -113555,7 +130745,7 @@ class value { * @returns INCORRECT_TYPE if the JSON value is not a string. Otherwise, we return SUCCESS. */ template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; /** * Cast this JSON value to a "wobbly" string. @@ -114076,7 +131266,7 @@ struct simdjson_result : public lasx::implementation_simd simdjson_inline simdjson_result get_double_in_string() noexcept; simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; simdjson_inline simdjson_result get_wobbly_string() noexcept; simdjson_inline simdjson_result get_raw_json_string() noexcept; simdjson_inline simdjson_result get_bool() noexcept; @@ -114157,7 +131347,22 @@ struct simdjson_result : public lasx::implementation_simd simdjson_result operator[](int) noexcept = delete; /** - * Get the type of this JSON value. + * Get the type of this JSON value. It does not validate or consume the value. + * E.g., you must still call "is_null()" to check that a value is null even if + * "type()" returns json_type::null. + * + * Given a valid JSON document, the answer can be one of + * simdjson::ondemand::json_type::object, + * simdjson::ondemand::json_type::array, + * simdjson::ondemand::json_type::string, + * simdjson::ondemand::json_type::number, + * simdjson::ondemand::json_type::boolean, + * simdjson::ondemand::json_type::null. + * + * Starting with simdjson 4.0, this function will return simdjson::ondemand::json_type::unknown + * given a bad token. + * This allows you to identify a case such as {"key": NaN} and identify the NaN value. + * The simdjson::ondemand::json_type::unknown value should only happen with non-valid JSON. * * NOTE: If you're only expecting a value to be one type (a typical case), it's generally * better to just call .get_double, .get_string, etc. and check for INCORRECT_TYPE (or just @@ -114651,14 +131856,14 @@ class json_iterator { * @param error The error to report. Must not be SUCCESS, UNINITIALIZED, INCORRECT_TYPE, or NO_SUCH_FIELD. * @param message An error message to report with the error. */ - simdjson_inline error_code report_error(error_code error, const char *message) noexcept; + simdjson_warn_unused simdjson_inline error_code report_error(error_code error, const char *message) noexcept; /** * Log error, but don't stop iteration. * @param error The error to report. Must be INCORRECT_TYPE, or NO_SUCH_FIELD. * @param message An error message to report with the error. */ - simdjson_inline error_code optional_error(error_code error, const char *message) noexcept; + simdjson_warn_unused simdjson_inline error_code optional_error(error_code error, const char *message) noexcept; /** * Take an input in json containing max_len characters and attempt to copy it over to tmpbuf, a buffer with @@ -114678,7 +131883,7 @@ class json_iterator { simdjson_inline void reenter_child(token_position position, depth_t child_depth) noexcept; - simdjson_inline error_code consume_character(char c) noexcept; + simdjson_warn_unused simdjson_inline error_code consume_character(char c) noexcept; #if SIMDJSON_DEVELOPMENT_CHECKS simdjson_inline token_position start_position(depth_t depth) const noexcept; simdjson_inline void set_start_position(depth_t depth, token_position position) noexcept; @@ -114769,6 +131974,7 @@ namespace ondemand { * The type of a JSON value. */ enum class json_type { + unknown=0, // Start at 1 to catch uninitialized / default values more easily array=1, ///< A JSON array ( [ 1, 2, 3 ... ] ) object, ///< A JSON object ( { "a": 1, "b" 2, ... } ) @@ -114975,6 +132181,12 @@ class raw_json_string { */ simdjson_inline const char * raw() const noexcept; + /** + * Get the character at index i. This is unchecked. + * [0] when the string is of length 0 returns the final quote ("). + */ + simdjson_inline char operator[](size_t i) const noexcept; + /** * This compares the current instance to the std::string_view target: returns true if * they are byte-by-byte equal (no escaping is done) on target.size() characters, @@ -115114,10 +132326,10 @@ struct simdjson_result : public lasx::implement simdjson_inline ~simdjson_result() noexcept = default; ///< @private simdjson_inline simdjson_result raw() const noexcept; + simdjson_inline char operator[](size_t) const noexcept; simdjson_inline simdjson_warn_unused simdjson_result unescape(lasx::ondemand::json_iterator &iter, bool allow_replacement) const noexcept; simdjson_inline simdjson_warn_unused simdjson_result unescape_wobbly(lasx::ondemand::json_iterator &iter) const noexcept; }; - } // namespace simdjson #endif // SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_H @@ -115133,6 +132345,7 @@ struct simdjson_result : public lasx::implement /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ #include +#include namespace simdjson { namespace lasx { @@ -115251,7 +132464,9 @@ class parser { simdjson_warn_unused simdjson_result iterate(std::string_view json, size_t capacity) & noexcept; /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ simdjson_warn_unused simdjson_result iterate(const std::string &json) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + /** @overload simdjson_result iterate(padded_string_view json) & noexcept + The string instance might be have its capacity extended. Note that this can still + result in AddressSanitizer: container-overflow in some cases. */ simdjson_warn_unused simdjson_result iterate(std::string &json) & noexcept; /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ simdjson_warn_unused simdjson_result iterate(const simdjson_result &json) & noexcept; @@ -115339,6 +132554,11 @@ class parser { * Setting batch_size to excessively large or excessively small values may impact negatively the * performance. * + * ### Threads + * + * When compiled with SIMDJSON_THREADS_ENABLED, this method will use a single thread under the + * hood to do some lookahead. + * * ### REQUIRED: Buffer Padding * * The buffer must have at least SIMDJSON_PADDING extra allocated bytes. It does not matter what @@ -115346,10 +132566,10 @@ class parser { * using a sanitizer that verifies that no uninitialized byte is read, then you should initialize the * SIMDJSON_PADDING bytes to avoid runtime warnings. * - * ### Threads + * This is checked automatically with all iterate_many function calls, except for the two + * that take pointers (const char* or const uint8_t*). * - * When compiled with SIMDJSON_THREADS_ENABLED, this method will use a single thread under the - * hood to do some lookahead. + * ### Threads * * ### Parser Capacity * @@ -115375,14 +132595,16 @@ class parser { */ inline simdjson_result iterate_many(const uint8_t *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ + inline simdjson_result iterate_many(padded_string_view json, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ inline simdjson_result iterate_many(const char *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ inline simdjson_result iterate_many(const std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; - inline simdjson_result iterate_many(const std::string &&s, size_t batch_size, bool allow_comma_separated = false) = delete;// unsafe + /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) + the string might be automatically padded with up to SIMDJSON_PADDING whitespace characters */ + inline simdjson_result iterate_many(std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ inline simdjson_result iterate_many(const padded_string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; - inline simdjson_result iterate_many(const padded_string &&s, size_t batch_size, bool allow_comma_separated = false) = delete;// unsafe - /** @private We do not want to allow implicit conversion from C string to std::string. */ simdjson_result iterate_many(const char *buf, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept = delete; @@ -115484,13 +132706,39 @@ class parser { bool string_buffer_overflow(const uint8_t *string_buf_loc) const noexcept; #endif + /** + * Get a unique parser instance corresponding to the current thread. + * This instance can be safely used within the current thread, but it should + * not be passed to other threads. + * + * A parser should only be used for one document at a time. + * + * Our simdjson::from functions use this parser instance. + * + * You can free the related parser by calling release_parser(). + */ + static simdjson_inline simdjson_warn_unused ondemand::parser& get_parser(); + /** + * Release the parser instance initialized by get_parser() and all the + * associated resources (memory). Returns true if a parser instance + * was released. + */ + static simdjson_inline bool release_parser(); + private: + friend bool release_parser(); + friend ondemand::parser& get_parser(); + /** Get the thread-local parser instance, allocates it if needed */ + static simdjson_inline simdjson_warn_unused std::unique_ptr& get_parser_instance(); + /** Get the thread-local parser instance, it might be null */ + static simdjson_inline simdjson_warn_unused std::unique_ptr& get_threadlocal_parser_if_exists(); /** @private [for benchmarking access] The implementation to use */ std::unique_ptr implementation{}; size_t _capacity{0}; size_t _max_capacity; size_t _max_depth{DEFAULT_MAX_DEPTH}; std::unique_ptr string_buf{}; + #if SIMDJSON_DEVELOPMENT_CHECKS std::unique_ptr start_positions{}; #endif @@ -115518,6 +132766,315 @@ struct simdjson_result : public lasx::implementation_sim #endif // SIMDJSON_GENERIC_ONDEMAND_PARSER_H /* end file simdjson/generic/ondemand/parser.h for lasx */ +// JSON builder - needed for extract_into functionality +/* including simdjson/generic/ondemand/json_string_builder.h for lasx: #include "simdjson/generic/ondemand/json_string_builder.h" */ +/* begin file simdjson/generic/ondemand/json_string_builder.h for lasx */ +/** + * This file is part of the builder API. It is temporarily in the ondemand directory + * but we will move it to a builder directory later. + */ +#ifndef SIMDJSON_GENERIC_STRING_BUILDER_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { + + +#if SIMDJSON_SUPPORTS_CONCEPTS + +namespace lasx { +namespace builder { + class string_builder; +}} + +template +struct has_custom_serialization : std::false_type {}; + +inline constexpr struct serialize_tag { + template + requires custom_deserializable + constexpr void operator()(lasx::builder::string_builder& b, T& obj) const{ + return tag_invoke(*this, b, obj); + } + + +} serialize{}; +template +struct has_custom_serialization(), std::declval())) +>> : std::true_type {}; + +template +constexpr bool require_custom_serialization = has_custom_serialization::value; +#else +struct has_custom_serialization : std::false_type {}; +#endif // SIMDJSON_SUPPORTS_CONCEPTS + +namespace lasx { +namespace builder { +/** + * A builder for JSON strings representing documents. This is a low-level + * builder that is not meant to be used directly by end-users. Though it + * supports atomic types (Booleans, strings), it does not support composed + * types (arrays and objects). + * + * Ultimately, this class can support kernel-specific optimizations. E.g., + * it may make use of SIMD instructions to escape strings faster. + */ +class string_builder { +public: + simdjson_inline string_builder(size_t initial_capacity = DEFAULT_INITIAL_CAPACITY); + + static constexpr size_t DEFAULT_INITIAL_CAPACITY = 1024; + + /** + * Append number (includes Booleans). Booleans are mapped to the strings + * false and true. Numbers are converted to strings abiding by the JSON standard. + * Floating-point numbers are converted to the shortest string that 'correctly' + * represents the number. + */ + template::value>::type> + simdjson_inline void append(number_type v) noexcept; + + /** + * Append character c. + */ + simdjson_inline void append(char c) noexcept; + + /** + * Append the string 'null'. + */ + simdjson_inline void append_null() noexcept; + + /** + * Clear the content. + */ + simdjson_inline void clear() noexcept; + + /** + * Append the std::string_view, after escaping it. + * There is no UTF-8 validation. + */ + simdjson_inline void escape_and_append(std::string_view input) noexcept; + + /** + * Append the std::string_view surrounded by double quotes, after escaping it. + * There is no UTF-8 validation. + */ + simdjson_inline void escape_and_append_with_quotes(std::string_view input) noexcept; +#if SIMDJSON_SUPPORTS_CONCEPTS + template + simdjson_inline void escape_and_append_with_quotes() noexcept; +#endif + /** + * Append the character surrounded by double quotes, after escaping it. + * There is no UTF-8 validation. + */ + simdjson_inline void escape_and_append_with_quotes(char input) noexcept; + + /** + * Append the character surrounded by double quotes, after escaping it. + * There is no UTF-8 validation. + */ + simdjson_inline void escape_and_append_with_quotes(const char* input) noexcept; + + /** + * Append the C string directly, without escaping. + * There is no UTF-8 validation. + */ + simdjson_inline void append_raw(const char *c) noexcept; + + /** + * Append "{" to the buffer. + */ + simdjson_inline void start_object() noexcept; + + /** + * Append "}" to the buffer. + */ + simdjson_inline void end_object() noexcept; + + /** + * Append "[" to the buffer. + */ + simdjson_inline void start_array() noexcept; + + /** + * Append "]" to the buffer. + */ + simdjson_inline void end_array() noexcept; + + /** + * Append "," to the buffer. + */ + simdjson_inline void append_comma() noexcept; + + /** + * Append ":" to the buffer. + */ + simdjson_inline void append_colon() noexcept; + + /** + * Append a key-value pair to the buffer. + * The key is escaped and surrounded by double quotes. + * The value is escaped if it is a string. + */ + template + simdjson_inline void append_key_value(key_type key, value_type value) noexcept; +#if SIMDJSON_SUPPORTS_CONCEPTS + template + simdjson_inline void append_key_value(value_type value) noexcept; + + // Support for optional types (std::optional, etc.) + template + requires(!require_custom_serialization) + simdjson_inline void append(const T &opt); + + template + requires(require_custom_serialization) + simdjson_inline void append(const T &val); + + // Support for string-like types + template + requires(std::is_convertible::value || + std::is_same::value ) + simdjson_inline void append(const T &value); +#endif +#if SIMDJSON_SUPPORTS_RANGES && SIMDJSON_SUPPORTS_CONCEPTS + // Support for range-based appending (std::ranges::view, etc.) + template +requires (!std::is_convertible::value) + simdjson_inline void append(const R &range) noexcept; +#endif + /** + * Append the std::string_view directly, without escaping. + * There is no UTF-8 validation. + */ + simdjson_inline void append_raw(std::string_view input) noexcept; + + /** + * Append len characters from str. + * There is no UTF-8 validation. + */ + simdjson_inline void append_raw(const char *str, size_t len) noexcept; +#if SIMDJSON_EXCEPTIONS + /** + * Creates an std::string from the written JSON buffer. + * Throws if memory allocation failed + * + * The result may not be valid UTF-8 if some of your content was not valid UTF-8. + * Use validate_unicode() to check the content if needed. + */ + simdjson_inline operator std::string() const noexcept(false); + + /** + * Creates an std::string_view from the written JSON buffer. + * Throws if memory allocation failed. + * + * The result may not be valid UTF-8 if some of your content was not valid UTF-8. + * Use validate_unicode() to check the content if needed. + */ + simdjson_inline operator std::string_view() const noexcept(false) simdjson_lifetime_bound; +#endif + + /** + * Returns a view on the written JSON buffer. Returns an error + * if memory allocation failed. + * + * The result may not be valid UTF-8 if some of your content was not valid UTF-8. + * Use validate_unicode() to check the content. + */ + simdjson_inline simdjson_result view() const noexcept; + + /** + * Appends the null character to the buffer and returns + * a pointer to the beginning of the written JSON buffer. + * Returns an error if memory allocation failed. + * The result is null-terminated. + * + * The result may not be valid UTF-8 if some of your content was not valid UTF-8. + * Use validate_unicode() to check the content. + */ + simdjson_inline simdjson_result c_str() noexcept; + + /** + * Return true if the content is valid UTF-8. + */ + simdjson_inline bool validate_unicode() const noexcept; + + /** + * Returns the current size of the written JSON buffer. + * If an error occurred, returns 0. + */ + simdjson_inline size_t size() const noexcept; + +private: + /** + * Returns true if we can write at least upcoming_bytes bytes. + * The underlying buffer is reallocated if needed. It is designed + * to be called before writing to the buffer. It should be fast. + */ + simdjson_inline bool capacity_check(size_t upcoming_bytes); + + /** + * Grow the buffer to at least desired_capacity bytes. + * If the allocation fails, is_valid is set to false. We expect + * that this function would not be repeatedly called. + */ + simdjson_inline void grow_buffer(size_t desired_capacity); + + /** + * We use this helper function to make sure that is_valid is kept consistent. + */ + simdjson_inline void set_valid(bool valid) noexcept; + + std::unique_ptr buffer{}; + size_t position{0}; + size_t capacity{0}; + bool is_valid{true}; +}; + + + +} +} + + +#if !SIMDJSON_STATIC_REFLECTION +// fallback implementation until we have static reflection +template +simdjson_warn_unused simdjson_result to_json(const Z &z, size_t initial_capacity = simdjson::lasx::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { + simdjson::lasx::builder::string_builder b(initial_capacity); + b.append(z); + std::string_view s; + auto e = b.view().get(s); + if(e) { return e; } + return std::string(s); +} +template +simdjson_warn_unused simdjson_error to_json(const Z &z, std::string &s, size_t initial_capacity = simdjson::lasx::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { + simdjson::lasx::builder::string_builder b(initial_capacity); + b.append(z); + std::string_view sv; + auto e = b.view().get(sv); + if(e) { return e; } + s.assign(sv.data(), sv.size()); + return simdjson::SUCCESS; +} +#endif + +#if SIMDJSON_SUPPORTS_CONCEPTS +#endif // SIMDJSON_SUPPORTS_CONCEPTS + +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_STRING_BUILDER_H +/* end file simdjson/generic/ondemand/json_string_builder.h for lasx */ + // All other declarations /* including simdjson/generic/ondemand/array.h for lasx: #include "simdjson/generic/ondemand/array.h" */ /* begin file simdjson/generic/ondemand/array.h for lasx */ @@ -115654,11 +133211,42 @@ class array { * - INDEX_OUT_OF_BOUNDS if the array index is larger than an array length */ simdjson_inline simdjson_result at(size_t index) noexcept; + +#if SIMDJSON_SUPPORTS_CONCEPTS + /** + * Get this array as the given type. + * + * @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized. + * @returns INCORRECT_TYPE If the JSON array is not of the given type. + * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. + */ + template + simdjson_warn_unused simdjson_inline error_code get(T &out) + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) { + static_assert(custom_deserializable); + return deserialize(*this, out); + } + /** + * Get this array as the given type. + * + * @returns A value of the given type, parsed from the JSON. + * @returns INCORRECT_TYPE If the JSON value is not the given type. + */ + template + simdjson_inline simdjson_result get() + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) + { + static_assert(std::is_default_constructible::value, "The specified type is not default constructible."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } +#endif // SIMDJSON_SUPPORTS_CONCEPTS protected: /** * Go to the end of the array, no matter where you are right now. */ - simdjson_inline error_code consume() noexcept; + simdjson_warn_unused simdjson_inline error_code consume() noexcept; /** * Begin array iteration. @@ -115732,7 +133320,28 @@ struct simdjson_result : public lasx::implementation_simd simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; simdjson_inline simdjson_result raw_json() noexcept; +#if SIMDJSON_SUPPORTS_CONCEPTS + // TODO: move this code into object-inl.h + template + simdjson_inline simdjson_result get() noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + return first; + } + return first.get(); + } + template + simdjson_warn_unused simdjson_inline error_code get(T& out) noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + out = first; + } else { + SIMDJSON_TRY( first.get(out) ); + } + return SUCCESS; + } +#endif // SIMDJSON_SUPPORTS_CONCEPTS }; } // namespace simdjson @@ -115777,7 +133386,8 @@ class array_iterator { * * Part of the std::iterator interface. */ - simdjson_inline simdjson_result operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. + simdjson_inline simdjson_result + operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. /** * Check if we are at the end of the JSON. * @@ -115801,6 +133411,11 @@ class array_iterator { */ simdjson_inline array_iterator &operator++() noexcept; + /** + * Check if the array is at the end. + */ + simdjson_warn_unused simdjson_inline bool at_end() const noexcept; + private: value_iterator iter{}; @@ -115819,7 +133434,6 @@ namespace simdjson { template<> struct simdjson_result : public lasx::implementation_simdjson_result_base { -public: simdjson_inline simdjson_result(lasx::ondemand::array_iterator &&value) noexcept; ///< @private simdjson_inline simdjson_result(error_code error) noexcept; ///< @private simdjson_inline simdjson_result() noexcept = default; @@ -115832,6 +133446,8 @@ struct simdjson_result : public lasx::implementa simdjson_inline bool operator==(const simdjson_result &) const noexcept; simdjson_inline bool operator!=(const simdjson_result &) const noexcept; simdjson_inline simdjson_result &operator++() noexcept; + + simdjson_warn_unused simdjson_inline bool at_end() const noexcept; }; } // namespace simdjson @@ -115959,7 +133575,7 @@ class document { * @returns INCORRECT_TYPE if the JSON value is not a string. Otherwise, we return SUCCESS. */ template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; /** * Cast this JSON value to a string. * @@ -116025,7 +133641,7 @@ class document { */ template simdjson_inline simdjson_result get() & -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept @@ -116048,7 +133664,7 @@ class document { */ template simdjson_inline simdjson_result get() && -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept @@ -116066,18 +133682,18 @@ class document { * Be mindful that the document instance must remain in scope while you are accessing object, array and value instances. * * @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized. - * @returns INCORRECT_TYPE If the JSON value is not an object. + * @returns INCORRECT_TYPE If the JSON value is of the given type. * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. */ template - simdjson_inline error_code get(T &out) & -#if SIMDJSON_SUPPORTS_DESERIALIZATION + simdjson_warn_unused simdjson_inline error_code get(T &out) & +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept #endif { -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS if constexpr (custom_deserializable) { return deserialize(*this, out); } else { @@ -116089,7 +133705,7 @@ class document { static_cast(out); // to get rid of unused errors return UNINITIALIZED; } -#else // SIMDJSON_SUPPORTS_DESERIALIZATION +#else // SIMDJSON_SUPPORTS_CONCEPTS // Unless the simdjson library or the user provides an inline implementation, calling this method should // immediately fail. static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " @@ -116099,7 +133715,7 @@ class document { " You may also add support for custom types, see our documentation."); static_cast(out); // to get rid of unused errors return UNINITIALIZED; -#endif // SIMDJSON_SUPPORTS_DESERIALIZATION +#endif // SIMDJSON_SUPPORTS_CONCEPTS } /** @overload template error_code get(T &out) & noexcept */ @@ -116164,7 +133780,7 @@ class document { * time it parses a document or when it is destroyed. * @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string. */ - simdjson_inline operator std::string_view() noexcept(false); + simdjson_inline operator std::string_view() noexcept(false) simdjson_lifetime_bound; /** * Cast this JSON value to a raw_json_string. * @@ -116173,7 +133789,7 @@ class document { * @returns A pointer to the raw JSON for the given string. * @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string. */ - simdjson_inline operator raw_json_string() noexcept(false); + simdjson_inline operator raw_json_string() noexcept(false) simdjson_lifetime_bound; /** * Cast this JSON value to a bool. * @@ -116323,11 +133939,27 @@ class document { * E.g., you must still call "is_null()" to check that a value is null even if * "type()" returns json_type::null. * + * The answer can be one of + * simdjson::ondemand::json_type::object, + * simdjson::ondemand::json_type::array, + * simdjson::ondemand::json_type::string, + * simdjson::ondemand::json_type::number, + * simdjson::ondemand::json_type::boolean, + * simdjson::ondemand::json_type::null. + * + * Starting with simdjson 4.0, this function will return simdjson::ondemand::json_type::unknown + * given a bad token. + * This allows you to identify a case such as {"key": NaN} and identify the NaN value. + * The simdjson::ondemand::json_type::unknown value should only happen with non-valid JSON. + * * NOTE: If you're only expecting a value to be one type (a typical case), it's generally * better to just call .get_double, .get_string, etc. and check for INCORRECT_TYPE (or just * let it throw an exception). * - * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". + * Prior to simdjson 4.0, this function would return an error given a bad token. + * Starting with simdjson 4.0, it will return simdjson::ondemand::json_type::unknown. + * This allows you to identify a case such as {"key": NaN} and identify the NaN value. + * The simdjson::ondemand::json_type::unknown value should only happen with non-valid JSON. */ simdjson_inline simdjson_result type() noexcept; @@ -116551,11 +134183,41 @@ class document { * the JSON document. */ simdjson_inline simdjson_result raw_json() noexcept; + +#if SIMDJSON_STATIC_REFLECTION + /** + * Extract only specific fields from the JSON object into a struct. + * + * This allows selective deserialization of only the fields you need, + * potentially improving performance by skipping unwanted fields. + * + * Example: + * ```c++ + * struct Car { + * std::string make; + * std::string model; + * int year; + * double price; + * }; + * + * Car car; + * doc.extract_into<"make", "model">(car); + * // Only 'make' and 'model' fields are extracted from JSON + * ``` + * + * @tparam FieldNames Compile-time string literals specifying which fields to extract + * @param out The output struct to populate with selected fields + * @returns SUCCESS on success, or an error code if a required field is missing or has wrong type + */ + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept; +#endif // SIMDJSON_STATIC_REFLECTION protected: /** * Consumes the document. */ - simdjson_inline error_code consume() noexcept; + simdjson_warn_unused simdjson_inline error_code consume() noexcept; simdjson_inline document(ondemand::json_iterator &&iter) noexcept; simdjson_inline const uint8_t *text(uint32_t idx) const noexcept; @@ -116608,7 +134270,7 @@ class document_reference { simdjson_inline simdjson_result get_double_in_string() noexcept; simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; simdjson_inline simdjson_result get_wobbly_string() noexcept; simdjson_inline simdjson_result get_raw_json_string() noexcept; simdjson_inline simdjson_result get_bool() noexcept; @@ -116617,7 +134279,7 @@ class document_reference { simdjson_inline simdjson_result is_null() noexcept; template simdjson_inline simdjson_result get() & -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept @@ -116630,7 +134292,7 @@ class document_reference { } template simdjson_inline simdjson_result get() && -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept @@ -116652,14 +134314,14 @@ class document_reference { * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. */ template - simdjson_inline error_code get(T &out) & -#if SIMDJSON_SUPPORTS_DESERIALIZATION + simdjson_warn_unused simdjson_inline error_code get(T &out) & +#if SIMDJSON_SUPPORTS_CONCEPTS noexcept(custom_deserializable ? nothrow_custom_deserializable : true) #else noexcept #endif { -#if SIMDJSON_SUPPORTS_DESERIALIZATION +#if SIMDJSON_SUPPORTS_CONCEPTS if constexpr (custom_deserializable) { return deserialize(*this, out); } else { @@ -116671,7 +134333,7 @@ class document_reference { static_cast(out); // to get rid of unused errors return UNINITIALIZED; } -#else // SIMDJSON_SUPPORTS_DESERIALIZATION +#else // SIMDJSON_SUPPORTS_CONCEPTS // Unless the simdjson library or the user provides an inline implementation, calling this method should // immediately fail. static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " @@ -116681,12 +134343,17 @@ class document_reference { " You may also add support for custom types, see our documentation."); static_cast(out); // to get rid of unused errors return UNINITIALIZED; -#endif // SIMDJSON_SUPPORTS_DESERIALIZATION +#endif // SIMDJSON_SUPPORTS_CONCEPTS } /** @overload template error_code get(T &out) & noexcept */ template simdjson_inline error_code get(T &out) && noexcept; simdjson_inline simdjson_result raw_json() noexcept; +#if SIMDJSON_STATIC_REFLECTION + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept; +#endif // SIMDJSON_STATIC_REFLECTION simdjson_inline operator document&() const noexcept; #if SIMDJSON_EXCEPTIONS template @@ -116755,7 +134422,7 @@ struct simdjson_result : public lasx::implementation_s simdjson_inline simdjson_result get_double_in_string() noexcept; simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; simdjson_inline simdjson_result get_wobbly_string() noexcept; simdjson_inline simdjson_result get_raw_json_string() noexcept; simdjson_inline simdjson_result get_bool() noexcept; @@ -116768,6 +134435,9 @@ struct simdjson_result : public lasx::implementation_s template simdjson_inline error_code get(T &out) & noexcept; template simdjson_inline error_code get(T &out) && noexcept; #if SIMDJSON_EXCEPTIONS + + using lasx::implementation_simdjson_result_base::operator*; + using lasx::implementation_simdjson_result_base::operator->; template ::value == false>::type> explicit simdjson_inline operator T() noexcept(false); simdjson_inline operator lasx::ondemand::array() & noexcept(false); @@ -116807,6 +134477,11 @@ struct simdjson_result : public lasx::implementation_s simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; +#if SIMDJSON_STATIC_REFLECTION + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept; +#endif // SIMDJSON_STATIC_REFLECTION }; @@ -116833,7 +134508,7 @@ struct simdjson_result : public lasx::implem simdjson_inline simdjson_result get_double_in_string() noexcept; simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; simdjson_inline simdjson_result get_wobbly_string() noexcept; simdjson_inline simdjson_result get_raw_json_string() noexcept; simdjson_inline simdjson_result get_bool() noexcept; @@ -116884,6 +134559,11 @@ struct simdjson_result : public lasx::implem simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; +#if SIMDJSON_STATIC_REFLECTION + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept; +#endif // SIMDJSON_STATIC_REFLECTION }; @@ -117026,6 +134706,7 @@ class document_stream { * Default constructor. */ simdjson_inline iterator() noexcept; + simdjson_inline iterator(const iterator &other) noexcept = default; /** * Get the current document (or error). */ @@ -117039,6 +134720,7 @@ class document_stream { * @param other the end iterator to compare to. */ simdjson_inline bool operator!=(const iterator &other) const noexcept; + simdjson_inline bool operator==(const iterator &other) const noexcept; /** * @private * @@ -117082,6 +134764,11 @@ class document_stream { */ inline error_code error() const noexcept; + /** + * Returns whether the iterator is at the end. + */ + inline bool at_end() const noexcept; + private: simdjson_inline iterator(document_stream *s, bool finished) noexcept; /** The document_stream we're iterating through. */ @@ -117093,6 +134780,7 @@ class document_stream { friend class document_stream; friend class json_iterator; }; + using iterator = document_stream::iterator; /** * Start iterating the documents in the stream. @@ -117356,6 +135044,9 @@ struct simdjson_result : public lasx::implementation_simd /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */ +/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION && SIMDJSON_SUPPORTS_CONCEPTS */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_string_builder.h" // for constevalutil::fixed_string */ +/* amalgamation skipped (editor-only): #endif */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { @@ -117553,11 +135244,71 @@ class object { */ simdjson_inline simdjson_result raw_json() noexcept; +#if SIMDJSON_SUPPORTS_CONCEPTS + /** + * Get this object as the given type. + * + * @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized. + * @returns INCORRECT_TYPE If the JSON object is not of the given type. + * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. + */ + template + simdjson_warn_unused simdjson_inline error_code get(T &out) + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) { + static_assert(custom_deserializable); + return deserialize(*this, out); + } + /** + * Get this array as the given type. + * + * @returns A value of the given type, parsed from the JSON. + * @returns INCORRECT_TYPE If the JSON value is not the given type. + */ + template + simdjson_inline simdjson_result get() + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) + { + static_assert(std::is_default_constructible::value, "The specified type is not default constructible."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } + +#if SIMDJSON_STATIC_REFLECTION + /** + * Extract only specific fields from the JSON object into a struct. + * + * This allows selective deserialization of only the fields you need, + * potentially improving performance by skipping unwanted fields. + * + * Example: + * ```c++ + * struct Car { + * std::string make; + * std::string model; + * int year; + * double price; + * }; + * + * Car car; + * object.extract_into<"make", "model">(car); + * // Only 'make' and 'model' fields are extracted from JSON + * ``` + * + * @tparam FieldNames Compile-time string literals specifying which fields to extract + * @param out The output struct to populate with selected fields + * @returns SUCCESS on success, or an error code if a required field is missing or has wrong type + */ + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept; +#endif // SIMDJSON_STATIC_REFLECTION +#endif // SIMDJSON_SUPPORTS_CONCEPTS protected: /** * Go to the end of the object, no matter where you are right now. */ - simdjson_inline error_code consume() noexcept; + simdjson_warn_unused simdjson_inline error_code consume() noexcept; static simdjson_inline simdjson_result start(value_iterator &iter) noexcept; static simdjson_inline simdjson_result start_root(value_iterator &iter) noexcept; static simdjson_inline simdjson_result started(value_iterator &iter) noexcept; @@ -117596,12 +135347,42 @@ struct simdjson_result : public lasx::implementation_sim simdjson_inline simdjson_result operator[](std::string_view key) && noexcept; simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; - inline simdjson_result reset() noexcept; inline simdjson_result is_empty() noexcept; inline simdjson_result count_fields() & noexcept; inline simdjson_result raw_json() noexcept; + #if SIMDJSON_SUPPORTS_CONCEPTS + // TODO: move this code into object-inl.h + template + simdjson_inline simdjson_result get() noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + return first; + } + return first.get(); + } + template + simdjson_warn_unused simdjson_inline error_code get(T& out) noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + out = first; + } else { + SIMDJSON_TRY( first.get(out) ); + } + return SUCCESS; + } + +#if SIMDJSON_STATIC_REFLECTION + // TODO: move this code into object-inl.h + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) noexcept { + if (error()) { return error(); } + return first.extract_into(out); + } +#endif // SIMDJSON_STATIC_REFLECTION +#endif // SIMDJSON_SUPPORTS_CONCEPTS }; } // namespace simdjson @@ -117730,6 +135511,20 @@ inline simdjson_result to_json_string(simdjson_result to_json_string(simdjson_result x); inline simdjson_result to_json_string(simdjson_result x); inline simdjson_result to_json_string(simdjson_result x); + +#if SIMDJSON_STATIC_REFLECTION +/** + * Create a JSON string from any user-defined type using static reflection. + * Only available when SIMDJSON_STATIC_REFLECTION is enabled. + */ +template + requires(!std::same_as && + !std::same_as && + !std::same_as && + !std::same_as) +inline std::string to_json_string(const T& obj); +#endif + } // namespace simdjson /** @@ -117801,28 +135596,30 @@ inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result #include +#if SIMDJSON_STATIC_REFLECTION +#include +// #include // for std::define_static_string - header not available yet +#endif namespace simdjson { -template -constexpr bool require_custom_serialization = false; ////////////////////////////// // Number deserialization ////////////////////////////// template - requires(!require_custom_serialization) error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { using limits = std::numeric_limits; @@ -117836,7 +135633,6 @@ error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { } template - requires(!require_custom_serialization) error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { double x; SIMDJSON_TRY(val.get_double().get(x)); @@ -117845,7 +135641,6 @@ error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { } template - requires(!require_custom_serialization) error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { using limits = std::numeric_limits; @@ -117858,8 +135653,23 @@ error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { return SUCCESS; } +////////////////////////////// +// String deserialization +////////////////////////////// + +// just a character! +error_code tag_invoke(deserialize_tag, auto &val, char &out) noexcept { + std::string_view x; + SIMDJSON_TRY(val.get_string().get(x)); + if(x.size() != 1) { + return INCORRECT_TYPE; + } + out = x[0]; + return SUCCESS; +} + +// any string-like type (can be constructed from std::string_view) template - requires(!require_custom_serialization) error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(std::is_nothrow_constructible_v) { std::string_view str; SIMDJSON_TRY(val.get_string().get(str)); @@ -117876,7 +135686,6 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(std::is_nothr * doc.get>(). */ template - requires(!require_custom_serialization) error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { using value_type = typename std::remove_cvref_t::value_type; static_assert( @@ -117885,9 +135694,13 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { static_assert( std::is_default_constructible_v, "The specified type inside the container must default constructible."); - lasx::ondemand::array arr; - SIMDJSON_TRY(val.get_array().get(arr)); + if constexpr (std::is_same_v, lasx::ondemand::array>) { + arr = val; + } else { + SIMDJSON_TRY(val.get_array().get(arr)); + } + for (auto v : arr) { if constexpr (concepts::returns_reference) { if (auto const err = v.get().get(concepts::emplace_one(out)); @@ -117918,7 +135731,6 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { * string-keyed types. */ template - requires(!require_custom_serialization) error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { using value_type = typename std::remove_cvref_t::mapped_type; static_assert( @@ -117944,7 +135756,45 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { return SUCCESS; } +template +error_code tag_invoke(deserialize_tag, lasx::ondemand::object &obj, T &out) noexcept { + using value_type = typename std::remove_cvref_t::mapped_type; + out.clear(); + for (auto field : obj) { + std::string_view key; + SIMDJSON_TRY(field.unescaped_key().get(key)); + + lasx::ondemand::value value_obj; + SIMDJSON_TRY(field.value().get(value_obj)); + + value_type this_value; + SIMDJSON_TRY(value_obj.get(this_value)); + out.emplace(typename T::key_type(key), std::move(this_value)); + } + return SUCCESS; +} + +template +error_code tag_invoke(deserialize_tag, lasx::ondemand::value &val, T &out) noexcept { + lasx::ondemand::object obj; + SIMDJSON_TRY(val.get_object().get(obj)); + return simdjson::deserialize(obj, out); +} + +template +error_code tag_invoke(deserialize_tag, lasx::ondemand::document &doc, T &out) noexcept { + lasx::ondemand::object obj; + SIMDJSON_TRY(doc.get_object().get(obj)); + return simdjson::deserialize(obj, out); +} + +template +error_code tag_invoke(deserialize_tag, lasx::ondemand::document_reference &doc, T &out) noexcept { + lasx::ondemand::object obj; + SIMDJSON_TRY(doc.get_object().get(obj)); + return simdjson::deserialize(obj, out); +} /** @@ -117962,7 +135812,6 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { * @return status of the conversion */ template - requires(!require_custom_serialization) error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deserializable::element_type, ValT>) { using element_type = typename std::remove_cvref_t::element_type; @@ -117987,17 +135836,17 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deser /** * This CPO (Customization Point Object) will help deserialize into optional types. */ -template - requires(!require_custom_serialization) -error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deserializable::value_type, ValT>) { +template +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept(nothrow_deserializable::value_type, decltype(val)>) { using value_type = typename std::remove_cvref_t::value_type; - static_assert( - deserializable, - "The specified type inside the unique_ptr must itself be deserializable"); - static_assert( - std::is_default_constructible_v, - "The specified type inside the unique_ptr must default constructible."); + // Check if the value is null + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); // Set to nullopt + return SUCCESS; + } if (!out) { out.emplace(); @@ -118006,10 +135855,329 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deser return SUCCESS; } + +#if SIMDJSON_STATIC_REFLECTION + + +template +constexpr bool user_defined_type = (std::is_class_v +&& !std::is_same_v && !std::is_same_v && !concepts::optional_type && +!concepts::appendable_containers); + + +template + requires(user_defined_type && std::is_class_v) +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept { + lasx::ondemand::object obj; + if constexpr (std::is_same_v, lasx::ondemand::object>) { + obj = val; + } else { + SIMDJSON_TRY(val.get_object().get(obj)); + } + template for (constexpr auto mem : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) { + if constexpr (!std::meta::is_const(mem) && std::meta::is_public(mem)) { + constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem)); + if constexpr (concepts::optional_type) { + // for optional members, it's ok if the key is missing + auto error = obj[key].get(out.[:mem:]); + if (error && error != NO_SUCH_FIELD) { + if(error == NO_SUCH_FIELD) { + out.[:mem:].reset(); + continue; + } + return error; + } + } else { + // for non-optional members, the key must be present + SIMDJSON_TRY(obj[key].get(out.[:mem:])); + } + } + }; + return simdjson::SUCCESS; +} + +// Support for enum deserialization - deserialize from string representation using expand approach from P2996R12 +template + requires(std::is_enum_v) +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept { +#if SIMDJSON_STATIC_REFLECTION + std::string_view str; + SIMDJSON_TRY(val.get_string().get(str)); + constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T)); + template for (constexpr auto enum_val : enumerators) { + if (str == std::meta::identifier_of(enum_val)) { + out = [:enum_val:]; + return SUCCESS; + } + }; + + return INCORRECT_TYPE; +#else + // Fallback: deserialize as integer if reflection not available + std::underlying_type_t int_val; + SIMDJSON_TRY(val.get(int_val)); + out = static_cast(int_val); + return SUCCESS; +#endif +} + +template + requires(user_defined_type>) +error_code tag_invoke(deserialize_tag, simdjson_value &val, std::unique_ptr &out) noexcept { + if (!out) { + out = std::make_unique(); + if (!out) { + return MEMALLOC; + } + } + if (auto err = val.get(*out)) { + out.reset(); + return err; + } + return SUCCESS; +} + +template + requires(user_defined_type>) +error_code tag_invoke(deserialize_tag, simdjson_value &val, std::shared_ptr &out) noexcept { + if (!out) { + out = std::make_shared(); + if (!out) { + return MEMALLOC; + } + } + if (auto err = val.get(*out)) { + out.reset(); + return err; + } + return SUCCESS; +} + +#endif // SIMDJSON_STATIC_REFLECTION + +//////////////////////////////////////// +// Unique pointers +//////////////////////////////////////// +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_unique(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_bool().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_unique(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_int64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_unique(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_uint64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_unique(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_double().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_unique(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_string().get(*out)); + return SUCCESS; +} + + +//////////////////////////////////////// +// Shared pointers +//////////////////////////////////////// +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_shared(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_bool().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_shared(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_int64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_shared(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_uint64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_shared(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_double().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); + return SUCCESS; + } + if (!out) { + out = std::make_shared(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_string().get(*out)); + return SUCCESS; +} + + +//////////////////////////////////////// +// Explicit optional specializations +//////////////////////////////////////// + +//////////////////////////////////////// +// Explicit smart pointer specializations for string and int types +//////////////////////////////////////// +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + // Check if the value is null + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); // Set to nullptr + return SUCCESS; + } + + if (!out) { + out = std::make_unique(); + } + std::string_view str; + SIMDJSON_TRY(val.get_string().get(str)); + *out = std::string{str}; + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &out) noexcept { + // Check if the value is null + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); // Set to nullptr + return SUCCESS; + } + + if (!out) { + out = std::make_shared(); + } + std::string_view str; + SIMDJSON_TRY(val.get_string().get(str)); + *out = std::string{str}; + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &out) noexcept { + // Check if the value is null + bool is_null_value; + SIMDJSON_TRY( val.is_null().get(is_null_value) ); + if (is_null_value) { + out.reset(); // Set to nullptr + return SUCCESS; + } + + if (!out) { + out = std::make_unique(); + } + int64_t temp; + SIMDJSON_TRY(val.get_int64().get(temp)); + *out = static_cast(temp); + return SUCCESS; +} + } // namespace simdjson #endif // SIMDJSON_ONDEMAND_DESERIALIZE_H -#endif // SIMDJSON_SUPPORTS_DESERIALIZATION +#endif // SIMDJSON_SUPPORTS_CONCEPTS /* end file simdjson/generic/ondemand/std_deserialize.h for lasx */ // Inline definitions @@ -118102,7 +136270,7 @@ simdjson_inline simdjson_result array::begin() noexcept { simdjson_inline simdjson_result array::end() noexcept { return array_iterator(iter); } -simdjson_inline error_code array::consume() noexcept { +simdjson_warn_unused simdjson_warn_unused simdjson_inline error_code array::consume() noexcept { auto error = iter.json_iter().skip_child(iter.depth()-1); if(error) { iter.abandon(); } return error; @@ -118293,6 +136461,9 @@ simdjson_inline array_iterator &array_iterator::operator++() noexcept { return *this; } +simdjson_inline bool array_iterator::at_end() const noexcept { + return iter.at_end(); +} } // namespace ondemand } // namespace lasx } // namespace simdjson @@ -118329,7 +136500,9 @@ simdjson_inline simdjson_result &simdjson_result ++(first); return *this; } - +simdjson_inline bool simdjson_result::at_end() const noexcept { + return !first.iter.is_valid() || first.at_end(); +} } // namespace simdjson #endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_INL_H @@ -118386,7 +136559,7 @@ simdjson_inline simdjson_result value::get_string(bool allow_r return iter.get_string(allow_replacement); } template -simdjson_inline error_code value::get_string(string_type& receiver, bool allow_replacement) noexcept { +simdjson_warn_unused simdjson_inline error_code value::get_string(string_type& receiver, bool allow_replacement) noexcept { return iter.get_string(receiver, allow_replacement); } simdjson_inline simdjson_result value::get_wobbly_string() noexcept { @@ -118428,15 +136601,15 @@ template<> simdjson_inline simdjson_result value::get() noexcept { retu template<> simdjson_inline simdjson_result value::get() noexcept { return get_bool(); } -template<> simdjson_inline error_code value::get(array& out) noexcept { return get_array().get(out); } -template<> simdjson_inline error_code value::get(object& out) noexcept { return get_object().get(out); } -template<> simdjson_inline error_code value::get(raw_json_string& out) noexcept { return get_raw_json_string().get(out); } -template<> simdjson_inline error_code value::get(std::string_view& out) noexcept { return get_string(false).get(out); } -template<> simdjson_inline error_code value::get(number& out) noexcept { return get_number().get(out); } -template<> simdjson_inline error_code value::get(double& out) noexcept { return get_double().get(out); } -template<> simdjson_inline error_code value::get(uint64_t& out) noexcept { return get_uint64().get(out); } -template<> simdjson_inline error_code value::get(int64_t& out) noexcept { return get_int64().get(out); } -template<> simdjson_inline error_code value::get(bool& out) noexcept { return get_bool().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(array& out) noexcept { return get_array().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(object& out) noexcept { return get_object().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(raw_json_string& out) noexcept { return get_raw_json_string().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(std::string_view& out) noexcept { return get_string(false).get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(number& out) noexcept { return get_number().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(double& out) noexcept { return get_double().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(uint64_t& out) noexcept { return get_uint64().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(int64_t& out) noexcept { return get_int64().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code value::get(bool& out) noexcept { return get_bool().get(out); } #if SIMDJSON_EXCEPTIONS template @@ -119033,7 +137206,7 @@ simdjson_inline simdjson_result document::get_string(bool allo return get_root_value_iterator().get_root_string(true, allow_replacement); } template -simdjson_inline error_code document::get_string(string_type& receiver, bool allow_replacement) noexcept { +simdjson_warn_unused simdjson_inline error_code document::get_string(string_type& receiver, bool allow_replacement) noexcept { return get_root_value_iterator().get_root_string(receiver, true, allow_replacement); } simdjson_inline simdjson_result document::get_wobbly_string() noexcept { @@ -119059,15 +137232,15 @@ template<> simdjson_inline simdjson_result document::get() & noexcept { template<> simdjson_inline simdjson_result document::get() & noexcept { return get_bool(); } template<> simdjson_inline simdjson_result document::get() & noexcept { return get_value(); } -template<> simdjson_inline error_code document::get(array& out) & noexcept { return get_array().get(out); } -template<> simdjson_inline error_code document::get(object& out) & noexcept { return get_object().get(out); } -template<> simdjson_inline error_code document::get(raw_json_string& out) & noexcept { return get_raw_json_string().get(out); } -template<> simdjson_inline error_code document::get(std::string_view& out) & noexcept { return get_string(false).get(out); } -template<> simdjson_inline error_code document::get(double& out) & noexcept { return get_double().get(out); } -template<> simdjson_inline error_code document::get(uint64_t& out) & noexcept { return get_uint64().get(out); } -template<> simdjson_inline error_code document::get(int64_t& out) & noexcept { return get_int64().get(out); } -template<> simdjson_inline error_code document::get(bool& out) & noexcept { return get_bool().get(out); } -template<> simdjson_inline error_code document::get(value& out) & noexcept { return get_value().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(array& out) & noexcept { return get_array().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(object& out) & noexcept { return get_object().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(raw_json_string& out) & noexcept { return get_raw_json_string().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(std::string_view& out) & noexcept { return get_string(false).get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(double& out) & noexcept { return get_double().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(uint64_t& out) & noexcept { return get_uint64().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(int64_t& out) & noexcept { return get_int64().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(bool& out) & noexcept { return get_bool().get(out); } +template<> simdjson_warn_unused simdjson_inline error_code document::get(value& out) & noexcept { return get_value().get(out); } template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_raw_json_string(); } template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_string(false); } @@ -119087,8 +137260,8 @@ simdjson_inline document::operator object() & noexcept(false) { return get_objec simdjson_inline document::operator uint64_t() noexcept(false) { return get_uint64(); } simdjson_inline document::operator int64_t() noexcept(false) { return get_int64(); } simdjson_inline document::operator double() noexcept(false) { return get_double(); } -simdjson_inline document::operator std::string_view() noexcept(false) { return get_string(false); } -simdjson_inline document::operator raw_json_string() noexcept(false) { return get_raw_json_string(); } +simdjson_inline document::operator std::string_view() noexcept(false) simdjson_lifetime_bound { return get_string(false); } +simdjson_inline document::operator raw_json_string() noexcept(false) simdjson_lifetime_bound { return get_raw_json_string(); } simdjson_inline document::operator bool() noexcept(false) { return get_bool(); } simdjson_inline document::operator value() noexcept(false) { return get_value(); } @@ -119137,7 +137310,7 @@ simdjson_inline simdjson_result document::operator[](const char *key) & n return start_or_resume_object()[key]; } -simdjson_inline error_code document::consume() noexcept { +simdjson_warn_unused simdjson_inline error_code document::consume() noexcept { bool scalar = false; auto error = is_scalar().get(scalar); if(error) { return error; } @@ -119239,6 +137412,54 @@ simdjson_inline simdjson_result document::at_path(std::string_view json_p } } + + +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION + +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code document::extract_into(T& out) & noexcept { + // Helper to check if a field name matches any of the requested fields + auto should_extract = [](std::string_view field_name) constexpr -> bool { + return ((FieldNames.view() == field_name) || ...); + }; + + // Iterate through all members of T using reflection + template for (constexpr auto mem : std::define_static_array( + std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) { + + if constexpr (!std::meta::is_const(mem) && std::meta::is_public(mem)) { + constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem)); + + // Only extract this field if it's in our list of requested fields + if constexpr (should_extract(key)) { + // Try to find and extract the field + if constexpr (concepts::optional_type) { + // For optional fields, it's ok if they're missing + auto field_result = find_field_unordered(key); + if (!field_result.error()) { + auto error = field_result.get(out.[:mem:]); + if (error && error != NO_SUCH_FIELD) { + return error; + } + } else if (field_result.error() != NO_SUCH_FIELD) { + return field_result.error(); + } else { + out.[:mem:].reset(); + } + } else { + // For required fields (in the requested list), fail if missing + SIMDJSON_TRY((*this)[key].get(out.[:mem:])); + } + } + } + }; + + return SUCCESS; +} + +#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION + } // namespace ondemand } // namespace lasx } // namespace simdjson @@ -119346,7 +137567,7 @@ simdjson_inline simdjson_result simdjson_result -simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { if (error()) { return error(); } return first.get_string(receiver, allow_replacement); } @@ -119382,12 +137603,12 @@ simdjson_deprecated simdjson_inline simdjson_result simdjson_result(first).get(); } template -simdjson_inline error_code simdjson_result::get(T &out) & noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) & noexcept { if (error()) { return error(); } return first.get(out); } template -simdjson_inline error_code simdjson_result::get(T &out) && noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) && noexcept { if (error()) { return error(); } return std::forward(first).get(out); } @@ -119397,8 +137618,8 @@ template<> simdjson_deprecated simdjson_inline simdjson_result(first); } -template<> simdjson_inline error_code simdjson_result::get(lasx::ondemand::document &out) & noexcept = delete; -template<> simdjson_inline error_code simdjson_result::get(lasx::ondemand::document &out) && noexcept { +template<> simdjson_warn_unused simdjson_inline error_code simdjson_result::get(lasx::ondemand::document &out) & noexcept = delete; +template<> simdjson_warn_unused simdjson_inline error_code simdjson_result::get(lasx::ondemand::document &out) && noexcept { if (error()) { return error(); } out = std::forward(first); return SUCCESS; @@ -119516,6 +137737,15 @@ simdjson_inline simdjson_result simdjson_result + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code simdjson_result::extract_into(T& out) & noexcept { + if (error()) { return error(); } + return first.extract_into(out); +} +#endif // SIMDJSON_STATIC_REFLECTION + } // namespace simdjson @@ -119550,7 +137780,7 @@ simdjson_inline simdjson_result document_reference::get_double() noexcep simdjson_inline simdjson_result document_reference::get_double_in_string() noexcept { return doc->get_root_value_iterator().get_root_double(false); } simdjson_inline simdjson_result document_reference::get_string(bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(false, allow_replacement); } template -simdjson_inline error_code document_reference::get_string(string_type& receiver, bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(receiver, false, allow_replacement); } +simdjson_warn_unused simdjson_inline error_code document_reference::get_string(string_type& receiver, bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(receiver, false, allow_replacement); } simdjson_inline simdjson_result document_reference::get_wobbly_string() noexcept { return doc->get_root_value_iterator().get_root_wobbly_string(false); } simdjson_inline simdjson_result document_reference::get_raw_json_string() noexcept { return doc->get_root_value_iterator().get_root_raw_json_string(false); } simdjson_inline simdjson_result document_reference::get_bool() noexcept { return doc->get_root_value_iterator().get_root_bool(false); } @@ -119603,7 +137833,13 @@ simdjson_inline simdjson_result document_reference::at_pointer(std::strin simdjson_inline simdjson_result document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); } simdjson_inline simdjson_result document_reference::raw_json() noexcept { return doc->raw_json();} simdjson_inline document_reference::operator document&() const noexcept { return *doc; } - +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code document_reference::extract_into(T& out) & noexcept { + return doc->extract_into(out); +} +#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION } // namespace ondemand } // namespace lasx } // namespace simdjson @@ -119700,7 +137936,7 @@ simdjson_inline simdjson_result simdjson_result -simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { if (error()) { return error(); } return first.get_string(receiver, allow_replacement); } @@ -119735,12 +137971,12 @@ simdjson_inline simdjson_result simdjson_result(first).get(); } template -simdjson_inline error_code simdjson_result::get(T &out) & noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) & noexcept { if (error()) { return error(); } return first.get(out); } template -simdjson_inline error_code simdjson_result::get(T &out) && noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) && noexcept { if (error()) { return error(); } return std::forward(first).get(out); } @@ -119757,13 +137993,13 @@ simdjson_inline simdjson_result simdjson_result -simdjson_inline error_code simdjson_result::get(lasx::ondemand::document_reference &out) & noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(lasx::ondemand::document_reference &out) & noexcept { if (error()) { return error(); } out = first; return SUCCESS; } template <> -simdjson_inline error_code simdjson_result::get(lasx::ondemand::document_reference &out) && noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(lasx::ondemand::document_reference &out) && noexcept { if (error()) { return error(); } out = first; return SUCCESS; @@ -119851,7 +138087,14 @@ simdjson_inline simdjson_result simdjson_result + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code simdjson_result::extract_into(T& out) & noexcept { + if (error()) { return error(); } + return first.extract_into(out); +} +#endif // SIMDJSON_STATIC_REFLECTION } // namespace simdjson #endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H @@ -120042,10 +138285,19 @@ simdjson_inline document_stream::iterator& document_stream::iterator::operator++ return *this; } +simdjson_inline bool document_stream::iterator::at_end() const noexcept { + return finished; +} + + simdjson_inline bool document_stream::iterator::operator!=(const document_stream::iterator &other) const noexcept { return finished != other.finished; } +simdjson_inline bool document_stream::iterator::operator==(const document_stream::iterator &other) const noexcept { + return finished == other.finished; +} + simdjson_inline document_stream::iterator document_stream::begin() noexcept { start(); // If there are no documents, we're finished. @@ -120163,7 +138415,10 @@ inline void document_stream::next_document() noexcept { // Always set depth=1 at the start of document doc.iter._depth = 1; // consume comma if comma separated is allowed - if (allow_comma_separated) { doc.iter.consume_character(','); } + if (allow_comma_separated) { + error_code ignored = doc.iter.consume_character(','); + static_cast(ignored); // ignored on purpose + } // Resets the string buffer at the beginning, thus invalidating the strings. doc.iter._string_buf_loc = parser->string_buf.get(); doc.iter._root = doc.iter.position(); @@ -120409,7 +138664,7 @@ simdjson_inline simdjson_result simdjson_result -simdjson_inline error_code simdjson_result::unescaped_key(string_type &receiver, bool allow_replacement) noexcept { +simdjson_warn_unused simdjson_inline error_code simdjson_result::unescaped_key(string_type &receiver, bool allow_replacement) noexcept { if (error()) { return error(); } return first.unescaped_key(receiver, allow_replacement); } @@ -120645,6 +138900,8 @@ simdjson_inline void json_iterator::assert_valid_position(token_position positio #ifndef SIMDJSON_CLANG_VISUAL_STUDIO SIMDJSON_ASSUME( position >= &parser->implementation->structural_indexes[0] ); SIMDJSON_ASSUME( position < &parser->implementation->structural_indexes[parser->implementation->n_structural_indexes] ); +#else + (void)position; // Suppress unused parameter warning #endif } @@ -120769,7 +139026,7 @@ simdjson_inline uint8_t *&json_iterator::string_buf_loc() noexcept { return _string_buf_loc; } -simdjson_inline error_code json_iterator::report_error(error_code _error, const char *message) noexcept { +simdjson_warn_unused simdjson_inline error_code json_iterator::report_error(error_code _error, const char *message) noexcept { SIMDJSON_ASSUME(_error != SUCCESS && _error != UNINITIALIZED && _error != INCORRECT_TYPE && _error != NO_SUCH_FIELD); logger::log_error(*this, message); error = _error; @@ -120813,7 +139070,7 @@ simdjson_inline void json_iterator::reenter_child(token_position position, depth _depth = child_depth; } -simdjson_inline error_code json_iterator::consume_character(char c) noexcept { +simdjson_warn_unused simdjson_inline error_code json_iterator::consume_character(char c) noexcept { if (*peek() == c) { return_current_and_advance(); return SUCCESS; @@ -120836,7 +139093,7 @@ simdjson_inline void json_iterator::set_start_position(depth_t depth, token_posi #endif -simdjson_inline error_code json_iterator::optional_error(error_code _error, const char *message) noexcept { +simdjson_warn_unused simdjson_inline error_code json_iterator::optional_error(error_code _error, const char *message) noexcept { SIMDJSON_ASSUME(_error == INCORRECT_TYPE || _error == NO_SUCH_FIELD); logger::log_error(*this, message); return _error; @@ -121231,6 +139488,10 @@ inline void log_line(const json_iterator &iter, token_position index, depth_t de /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value-inl.h" */ +/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_string_builder.h" // for constevalutil::fixed_string */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #endif */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { @@ -121288,7 +139549,7 @@ simdjson_inline simdjson_result object::start_root(value_iterator &iter) SIMDJSON_TRY( iter.start_root_object().error() ); return object(iter); } -simdjson_inline error_code object::consume() noexcept { +simdjson_warn_unused simdjson_inline error_code object::consume() noexcept { if(iter.is_at_key()) { /** * whenever you are pointing at a key, calling skip_child() is @@ -121417,6 +139678,52 @@ simdjson_inline simdjson_result object::reset() & noexcept { return iter.reset_object(); } +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION + +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code object::extract_into(T& out) & noexcept { + // Helper to check if a field name matches any of the requested fields + auto should_extract = [](std::string_view field_name) constexpr -> bool { + return ((FieldNames.view() == field_name) || ...); + }; + + // Iterate through all members of T using reflection + template for (constexpr auto mem : std::define_static_array( + std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) { + + if constexpr (!std::meta::is_const(mem) && std::meta::is_public(mem)) { + constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem)); + + // Only extract this field if it's in our list of requested fields + if constexpr (should_extract(key)) { + // Try to find and extract the field + if constexpr (concepts::optional_type) { + // For optional fields, it's ok if they're missing + auto field_result = find_field_unordered(key); + if (!field_result.error()) { + auto error = field_result.get(out.[:mem:]); + if (error && error != NO_SUCH_FIELD) { + return error; + } + } else if (field_result.error() != NO_SUCH_FIELD) { + return field_result.error(); + } else { + out.[:mem:].reset(); + } + } else { + // For required fields (in the requested list), fail if missing + SIMDJSON_TRY((*this)[key].get(out.[:mem:])); + } + } + } + }; + + return SUCCESS; +} + +#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION + } // namespace ondemand } // namespace lasx } // namespace simdjson @@ -121493,6 +139800,7 @@ simdjson_inline simdjson_result simdjson_result parser::iterate(padded_string_view json) & noexcept { - if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; } + if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; } json.remove_utf8_bom(); @@ -121707,7 +140015,7 @@ simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(p #ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON simdjson_warn_unused simdjson_inline simdjson_result parser::iterate_allow_incomplete_json(padded_string_view json) & noexcept { - if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; } + if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; } json.remove_utf8_bom(); @@ -121739,10 +140047,7 @@ simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(s } simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(std::string &json) & noexcept { - if(json.capacity() - json.size() < SIMDJSON_PADDING) { - json.reserve(json.size() + SIMDJSON_PADDING); - } - return iterate(padded_string_view(json)); + return iterate(pad_with_reserve(json)); } simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(const std::string &json) & noexcept { @@ -121764,7 +140069,7 @@ simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(c } simdjson_warn_unused simdjson_inline simdjson_result parser::iterate_raw(padded_string_view json) & noexcept { - if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; } + if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; } json.remove_utf8_bom(); @@ -121779,6 +140084,7 @@ simdjson_warn_unused simdjson_inline simdjson_result parser::iter } inline simdjson_result parser::iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept { + // Warning: no check is done on the buffer padding. We trust the user. if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; } if((len >= 3) && (std::memcmp(buf, "\xEF\xBB\xBF", 3) == 0)) { buf += 3; @@ -121787,16 +140093,24 @@ inline simdjson_result parser::iterate_many(const uint8_t *buf, if(allow_comma_separated && batch_size < len) { batch_size = len; } return document_stream(*this, buf, len, batch_size, allow_comma_separated); } + inline simdjson_result parser::iterate_many(const char *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept { + // Warning: no check is done on the buffer padding. We trust the user. return iterate_many(reinterpret_cast(buf), len, batch_size, allow_comma_separated); } -inline simdjson_result parser::iterate_many(const std::string &s, size_t batch_size, bool allow_comma_separated) noexcept { +inline simdjson_result parser::iterate_many(padded_string_view s, size_t batch_size, bool allow_comma_separated) noexcept { + if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; } return iterate_many(s.data(), s.length(), batch_size, allow_comma_separated); } inline simdjson_result parser::iterate_many(const padded_string &s, size_t batch_size, bool allow_comma_separated) noexcept { - return iterate_many(s.data(), s.length(), batch_size, allow_comma_separated); + return iterate_many(padded_string_view(s), batch_size, allow_comma_separated); +} +inline simdjson_result parser::iterate_many(const std::string &s, size_t batch_size, bool allow_comma_separated) noexcept { + return iterate_many(padded_string_view(s), batch_size, allow_comma_separated); +} +inline simdjson_result parser::iterate_many(std::string &s, size_t batch_size, bool allow_comma_separated) noexcept { + return iterate_many(pad(s), batch_size, allow_comma_separated); } - simdjson_pure simdjson_inline size_t parser::capacity() const noexcept { return _capacity; } @@ -121831,6 +140145,34 @@ simdjson_inline simdjson_warn_unused simdjson_result parser::u return result; } +simdjson_inline simdjson_warn_unused ondemand::parser& parser::get_parser() { + return *parser::get_parser_instance(); +} + +simdjson_inline bool release_parser() { + auto &parser_instance = parser::get_threadlocal_parser_if_exists(); + if (parser_instance) { + parser_instance.reset(); + return true; + } + return false; +} + +simdjson_inline simdjson_warn_unused std::unique_ptr& parser::get_parser_instance() { + std::unique_ptr& parser_instance = get_threadlocal_parser_if_exists(); + if (!parser_instance) { + parser_instance.reset(new ondemand::parser()); + } + return parser_instance; +} + +simdjson_inline simdjson_warn_unused std::unique_ptr& parser::get_threadlocal_parser_if_exists() { + // @the-moisrex points out that this could be implemented with std::optional (C++17). + thread_local std::unique_ptr parser_instance = nullptr; + return parser_instance; +} + + } // namespace ondemand } // namespace lasx } // namespace simdjson @@ -121865,8 +140207,13 @@ namespace ondemand { simdjson_inline raw_json_string::raw_json_string(const uint8_t * _buf) noexcept : buf{_buf} {} -simdjson_inline const char * raw_json_string::raw() const noexcept { return reinterpret_cast(buf); } +simdjson_inline const char * raw_json_string::raw() const noexcept { + return reinterpret_cast(buf); +} +simdjson_inline char raw_json_string::operator[](size_t i) const noexcept { + return reinterpret_cast(buf)[i]; +} simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(std::string_view target) noexcept { size_t pos{0}; @@ -122043,6 +140390,10 @@ simdjson_inline simdjson_result simdjson_result::operator[](size_t i) const noexcept { + if (error()) { return error(); } + return first[i]; +} simdjson_inline simdjson_warn_unused simdjson_result simdjson_result::unescape(lasx::ondemand::json_iterator &iter, bool allow_replacement) const noexcept { if (error()) { return error(); } return first.unescape(iter, allow_replacement); @@ -122068,6 +140419,9 @@ simdjson_inline simdjson_warn_unused simdjson_result simdjson_ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/serialization.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */ +/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_builder.h" */ +/* amalgamation skipped (editor-only): #endif */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { @@ -122433,7 +140787,7 @@ simdjson_warn_unused simdjson_inline simdjson_result value_iterator::start if (*_json_iter->peek() == '}') { logger::log_value(*_json_iter, "empty object"); _json_iter->return_current_and_advance(); - end_container(); + SIMDJSON_TRY(end_container()); return false; } return true; @@ -123287,7 +141641,7 @@ simdjson_inline void value_iterator::advance_scalar(const char *type) noexcept { _json_iter->ascend_to(depth()-1); } -simdjson_inline error_code value_iterator::start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept { +simdjson_warn_unused simdjson_inline error_code value_iterator::start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept { logger::log_start_value(*_json_iter, start_position(), depth(), type); // If we're not at the position anymore, we don't want to advance the cursor. const uint8_t *json; @@ -123449,7 +141803,7 @@ simdjson_inline simdjson_result value_iterator::type() const noexcept case '5': case '6': case '7': case '8': case '9': return json_type::number; default: - return TAPE_ERROR; + return json_type::unknown; } } @@ -123489,6 +141843,1097 @@ simdjson_inline simdjson_result::simdjson_result #endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H /* end file simdjson/generic/ondemand/value_iterator-inl.h for lasx */ +// JSON builder inline definitions +/* including simdjson/generic/ondemand/json_string_builder-inl.h for lasx: #include "simdjson/generic/ondemand/json_string_builder-inl.h" */ +/* begin file simdjson/generic/ondemand/json_string_builder-inl.h for lasx */ +/** + * This file is part of the builder API. It is temporarily in the ondemand + * directory but we will move it to a builder directory later. + */ +#include +#include +#include +#ifndef SIMDJSON_GENERIC_STRING_BUILDER_INL_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_INL_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_string_builder.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +/* + * Empirically, we have found that an inlined optimization is important for + * performance. The following macros are not ideal. We should find a better + * way to inline the code. + */ + +#if defined(__SSE2__) || defined(__x86_64__) || defined(__x86_64) || \ + (defined(_M_AMD64) || defined(_M_X64) || \ + (defined(_M_IX86_FP) && _M_IX86_FP == 2)) +#ifndef SIMDJSON_EXPERIMENTAL_HAS_SSE2 +#define SIMDJSON_EXPERIMENTAL_HAS_SSE2 1 +#endif +#endif + +#if defined(__aarch64__) || defined(_M_ARM64) +#ifndef SIMDJSON_EXPERIMENTAL_HAS_NEON +#define SIMDJSON_EXPERIMENTAL_HAS_NEON 1 +#endif +#endif +#if SIMDJSON_EXPERIMENTAL_HAS_NEON +#include +#endif +#if SIMDJSON_EXPERIMENTAL_HAS_SSE2 +#include +#endif + +namespace simdjson { +namespace lasx { +namespace builder { + +static SIMDJSON_CONSTEXPR_LAMBDA std::array + json_quotable_character = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 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, 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, 1, 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, 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, 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, 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, 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, 0, 0, 0}; + +/** + +A possible SWAR implementation of has_json_escapable_byte. It is not used +because it is slower than the current implementation. It is kept here for +reference (to show that we tried it). + +inline bool has_json_escapable_byte(uint64_t x) { + uint64_t is_ascii = 0x8080808080808080ULL & ~x; + uint64_t xor2 = x ^ 0x0202020202020202ULL; + uint64_t lt32_or_eq34 = xor2 - 0x2121212121212121ULL; + uint64_t sub92 = x ^ 0x5C5C5C5C5C5C5C5CULL; + uint64_t eq92 = (sub92 - 0x0101010101010101ULL); + return ((lt32_or_eq34 | eq92) & is_ascii) != 0; +} + +**/ + +SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline bool +simple_needs_escaping(std::string_view v) { + for (char c : v) { + // a table lookup is faster than a series of comparisons + if (json_quotable_character[static_cast(c)]) { + return true; + } + } + return false; +} + +#if SIMDJSON_EXPERIMENTAL_HAS_NEON +simdjson_inline bool fast_needs_escaping(std::string_view view) { + if (view.size() < 16) { + return simple_needs_escaping(view); + } + size_t i = 0; + uint8x16_t running = vdupq_n_u8(0); + uint8x16_t v34 = vdupq_n_u8(34); + uint8x16_t v92 = vdupq_n_u8(92); + + for (; i + 15 < view.size(); i += 16) { + uint8x16_t word = vld1q_u8((const uint8_t *)view.data() + i); + running = vorrq_u8(running, vceqq_u8(word, v34)); + running = vorrq_u8(running, vceqq_u8(word, v92)); + running = vorrq_u8(running, vcltq_u8(word, vdupq_n_u8(32))); + } + if (i < view.size()) { + uint8x16_t word = + vld1q_u8((const uint8_t *)view.data() + view.length() - 16); + running = vorrq_u8(running, vceqq_u8(word, v34)); + running = vorrq_u8(running, vceqq_u8(word, v92)); + running = vorrq_u8(running, vcltq_u8(word, vdupq_n_u8(32))); + } + return vmaxvq_u32(vreinterpretq_u32_u8(running)) != 0; +} +#elif SIMDJSON_EXPERIMENTAL_HAS_SSE2 +simdjson_inline bool fast_needs_escaping(std::string_view view) { + if (view.size() < 16) { + return simple_needs_escaping(view); + } + size_t i = 0; + __m128i running = _mm_setzero_si128(); + for (; i + 15 < view.size(); i += 16) { + + __m128i word = + _mm_loadu_si128(reinterpret_cast(view.data() + i)); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(34))); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(92))); + running = _mm_or_si128( + running, _mm_cmpeq_epi8(_mm_subs_epu8(word, _mm_set1_epi8(31)), + _mm_setzero_si128())); + } + if (i < view.size()) { + __m128i word = _mm_loadu_si128( + reinterpret_cast(view.data() + view.length() - 16)); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(34))); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(92))); + running = _mm_or_si128( + running, _mm_cmpeq_epi8(_mm_subs_epu8(word, _mm_set1_epi8(31)), + _mm_setzero_si128())); + } + return _mm_movemask_epi8(running) != 0; +} +#else +simdjson_inline bool fast_needs_escaping(std::string_view view) { + return simple_needs_escaping(view); +} +#endif + +SIMDJSON_CONSTEXPR_LAMBDA inline size_t +find_next_json_quotable_character(const std::string_view view, + size_t location) noexcept { + + for (auto pos = view.begin() + location; pos != view.end(); ++pos) { + if (json_quotable_character[static_cast(*pos)]) { + return pos - view.begin(); + } + } + return size_t(view.size()); +} + +SIMDJSON_CONSTEXPR_LAMBDA static std::string_view control_chars[] = { + "\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", "\\u0006", + "\\u0007", "\\b", "\\t", "\\n", "\\u000b", "\\f", "\\r", + "\\u000e", "\\u000f", "\\u0010", "\\u0011", "\\u0012", "\\u0013", "\\u0014", + "\\u0015", "\\u0016", "\\u0017", "\\u0018", "\\u0019", "\\u001a", "\\u001b", + "\\u001c", "\\u001d", "\\u001e", "\\u001f"}; + +// All Unicode characters may be placed within the quotation marks, except for +// the characters that MUST be escaped: quotation mark, reverse solidus, and the +// control characters (U+0000 through U+001F). There are two-character sequence +// escape representations of some popular characters: +// \", \\, \b, \f, \n, \r, \t. +SIMDJSON_CONSTEXPR_LAMBDA void escape_json_char(char c, char *&out) { + if (c == '"') { + memcpy(out, "\\\"", 2); + out += 2; + } else if (c == '\\') { + memcpy(out, "\\\\", 2); + out += 2; + } else { + std::string_view v = control_chars[uint8_t(c)]; + memcpy(out, v.data(), v.size()); + out += v.size(); + } +} + +inline size_t write_string_escaped(const std::string_view input, char *out) { + size_t mysize = input.size(); + if (!fast_needs_escaping(input)) { // fast path! + memcpy(out, input.data(), input.size()); + return input.size(); + } + const char *const initout = out; + size_t location = find_next_json_quotable_character(input, 0); + memcpy(out, input.data(), location); + out += location; + escape_json_char(input[location], out); + location += 1; + while (location < mysize) { + size_t newlocation = find_next_json_quotable_character(input, location); + memcpy(out, input.data() + location, newlocation - location); + out += newlocation - location; + location = newlocation; + if (location == mysize) { + break; + } + escape_json_char(input[location], out); + location += 1; + } + return out - initout; +} + +simdjson_inline string_builder::string_builder(size_t initial_capacity) + : buffer(new(std::nothrow) char[initial_capacity]), position(0), + capacity(buffer.get() != nullptr ? initial_capacity : 0), + is_valid(buffer.get() != nullptr) {} + +simdjson_inline bool string_builder::capacity_check(size_t upcoming_bytes) { + // We use the convention that when is_valid is false, then the capacity and + // the position are 0. + // Most of the time, this function will return true. + if (simdjson_likely(upcoming_bytes <= capacity - position)) { + return true; + } + // check for overflow, most of the time there is no overflow + if (simdjson_likely(position + upcoming_bytes < position)) { + return false; + } + // We will rarely get here. + grow_buffer((std::max)(capacity * 2, position + upcoming_bytes)); + // If the buffer allocation failed, we set is_valid to false. + return is_valid; +} + +simdjson_inline void string_builder::grow_buffer(size_t desired_capacity) { + if (!is_valid) { + return; + } + std::unique_ptr new_buffer(new (std::nothrow) char[desired_capacity]); + if (new_buffer.get() == nullptr) { + set_valid(false); + return; + } + std::memcpy(new_buffer.get(), buffer.get(), position); + buffer.swap(new_buffer); + capacity = desired_capacity; +} + +simdjson_inline void string_builder::set_valid(bool valid) noexcept { + if (!valid) { + is_valid = false; + capacity = 0; + position = 0; + buffer.reset(); + } else { + is_valid = true; + } +} + +simdjson_inline size_t string_builder::size() const noexcept { + return position; +} + +simdjson_inline void string_builder::append(char c) noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = c; + } +} + +simdjson_inline void string_builder::append_null() noexcept { + constexpr char null_literal[] = "null"; + constexpr size_t null_len = sizeof(null_literal) - 1; + if (capacity_check(null_len)) { + std::memcpy(buffer.get() + position, null_literal, null_len); + position += null_len; + } +} + +simdjson_inline void string_builder::clear() noexcept { + position = 0; + // if it was invalid, we should try to repair it + if (!is_valid) { + capacity = 0; + buffer.reset(); + is_valid = true; + } +} + +namespace internal { + +template ::value>::type> +simdjson_really_inline int int_log2(number_type x) { + return 63 - leading_zeroes(uint64_t(x) | 1); +} + +simdjson_really_inline int fast_digit_count_32(uint32_t x) { + static uint64_t table[] = { + 4294967296, 8589934582, 8589934582, 8589934582, 12884901788, + 12884901788, 12884901788, 17179868184, 17179868184, 17179868184, + 21474826480, 21474826480, 21474826480, 21474826480, 25769703776, + 25769703776, 25769703776, 30063771072, 30063771072, 30063771072, + 34349738368, 34349738368, 34349738368, 34349738368, 38554705664, + 38554705664, 38554705664, 41949672960, 41949672960, 41949672960, + 42949672960, 42949672960}; + return uint32_t((x + table[int_log2(x)]) >> 32); +} + +simdjson_really_inline int fast_digit_count_64(uint64_t x) { + static uint64_t table[] = {9, + 99, + 999, + 9999, + 99999, + 999999, + 9999999, + 99999999, + 999999999, + 9999999999, + 99999999999, + 999999999999, + 9999999999999, + 99999999999999, + 999999999999999ULL, + 9999999999999999ULL, + 99999999999999999ULL, + 999999999999999999ULL, + 9999999999999999999ULL}; + int y = (19 * int_log2(x) >> 6); + y += x > table[y]; + return y + 1; +} + +template ::value>::type> +simdjson_really_inline size_t digit_count(number_type v) noexcept { + static_assert(sizeof(number_type) == 8 || sizeof(number_type) == 4 || + sizeof(number_type) == 2 || sizeof(number_type) == 1, + "We only support 8-bit, 16-bit, 32-bit and 64-bit numbers"); + SIMDJSON_IF_CONSTEXPR(sizeof(number_type) <= 4) { + return fast_digit_count_32(static_cast(v)); + } + else { + return fast_digit_count_64(static_cast(v)); + } +} +static const char decimal_table[200] = { + 0x30, 0x30, 0x30, 0x31, 0x30, 0x32, 0x30, 0x33, 0x30, 0x34, 0x30, 0x35, + 0x30, 0x36, 0x30, 0x37, 0x30, 0x38, 0x30, 0x39, 0x31, 0x30, 0x31, 0x31, + 0x31, 0x32, 0x31, 0x33, 0x31, 0x34, 0x31, 0x35, 0x31, 0x36, 0x31, 0x37, + 0x31, 0x38, 0x31, 0x39, 0x32, 0x30, 0x32, 0x31, 0x32, 0x32, 0x32, 0x33, + 0x32, 0x34, 0x32, 0x35, 0x32, 0x36, 0x32, 0x37, 0x32, 0x38, 0x32, 0x39, + 0x33, 0x30, 0x33, 0x31, 0x33, 0x32, 0x33, 0x33, 0x33, 0x34, 0x33, 0x35, + 0x33, 0x36, 0x33, 0x37, 0x33, 0x38, 0x33, 0x39, 0x34, 0x30, 0x34, 0x31, + 0x34, 0x32, 0x34, 0x33, 0x34, 0x34, 0x34, 0x35, 0x34, 0x36, 0x34, 0x37, + 0x34, 0x38, 0x34, 0x39, 0x35, 0x30, 0x35, 0x31, 0x35, 0x32, 0x35, 0x33, + 0x35, 0x34, 0x35, 0x35, 0x35, 0x36, 0x35, 0x37, 0x35, 0x38, 0x35, 0x39, + 0x36, 0x30, 0x36, 0x31, 0x36, 0x32, 0x36, 0x33, 0x36, 0x34, 0x36, 0x35, + 0x36, 0x36, 0x36, 0x37, 0x36, 0x38, 0x36, 0x39, 0x37, 0x30, 0x37, 0x31, + 0x37, 0x32, 0x37, 0x33, 0x37, 0x34, 0x37, 0x35, 0x37, 0x36, 0x37, 0x37, + 0x37, 0x38, 0x37, 0x39, 0x38, 0x30, 0x38, 0x31, 0x38, 0x32, 0x38, 0x33, + 0x38, 0x34, 0x38, 0x35, 0x38, 0x36, 0x38, 0x37, 0x38, 0x38, 0x38, 0x39, + 0x39, 0x30, 0x39, 0x31, 0x39, 0x32, 0x39, 0x33, 0x39, 0x34, 0x39, 0x35, + 0x39, 0x36, 0x39, 0x37, 0x39, 0x38, 0x39, 0x39, +}; +} // namespace internal + +template +simdjson_inline void string_builder::append(number_type v) noexcept { + static_assert(std::is_same::value || + std::is_integral::value || + std::is_floating_point::value, + "Unsupported number type"); + // If C++17 is available, we can 'if constexpr' here. + SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + if (v) { + constexpr char true_literal[] = "true"; + constexpr size_t true_len = sizeof(true_literal) - 1; + if (capacity_check(true_len)) { + std::memcpy(buffer.get() + position, true_literal, true_len); + position += true_len; + } + } else { + constexpr char false_literal[] = "false"; + constexpr size_t false_len = sizeof(false_literal) - 1; + if (capacity_check(false_len)) { + std::memcpy(buffer.get() + position, false_literal, false_len); + position += false_len; + } + } + } + else SIMDJSON_IF_CONSTEXPR(std::is_unsigned::value) { + constexpr size_t max_number_size = 20; + if (capacity_check(max_number_size)) { + using unsigned_type = typename std::make_unsigned::type; + unsigned_type pv = static_cast(v); + size_t dc = internal::digit_count(pv); + char *write_pointer = buffer.get() + position + dc - 1; + while (pv >= 100) { + memcpy(write_pointer - 1, &internal::decimal_table[(pv % 100) * 2], 2); + write_pointer -= 2; + pv /= 100; + } + if (pv >= 10) { + *write_pointer-- = char('0' + (pv % 10)); + pv /= 10; + } + *write_pointer = char('0' + pv); + position += dc; + } + } + else SIMDJSON_IF_CONSTEXPR(std::is_integral::value) { + constexpr size_t max_number_size = 20; + if (capacity_check(max_number_size)) { + using unsigned_type = typename std::make_unsigned::type; + bool negative = v < 0; + unsigned_type pv = static_cast(v); + if (negative) { + pv = 0 - pv; // the 0 is for Microsoft + } + size_t dc = internal::digit_count(pv); + if (negative) { + buffer.get()[position++] = '-'; + } + char *write_pointer = buffer.get() + position + dc - 1; + while (pv >= 100) { + memcpy(write_pointer - 1, &internal::decimal_table[(pv % 100) * 2], 2); + write_pointer -= 2; + pv /= 100; + } + if (pv >= 10) { + *write_pointer-- = char('0' + (pv % 10)); + pv /= 10; + } + *write_pointer = char('0' + pv); + position += dc; + } + } + else SIMDJSON_IF_CONSTEXPR(std::is_floating_point::value) { + constexpr size_t max_number_size = 24; + if (capacity_check(max_number_size)) { + // We could specialize for float. + char *end = simdjson::internal::to_chars(buffer.get() + position, nullptr, + double(v)); + position = end - buffer.get(); + } + } +} + +simdjson_inline void +string_builder::escape_and_append(std::string_view input) noexcept { + // escaping might turn a control character into \x00xx so 6 characters. + if (capacity_check(6 * input.size())) { + position += write_string_escaped(input, buffer.get() + position); + } +} + +simdjson_inline void +string_builder::escape_and_append_with_quotes(std::string_view input) noexcept { + // escaping might turn a control character into \x00xx so 6 characters. + if (capacity_check(2 + 6 * input.size())) { + buffer.get()[position++] = '"'; + position += write_string_escaped(input, buffer.get() + position); + buffer.get()[position++] = '"'; + } +} + +simdjson_inline void +string_builder::escape_and_append_with_quotes(char input) noexcept { + // escaping might turn a control character into \x00xx so 6 characters. + if (capacity_check(2 + 6 * 1)) { + buffer.get()[position++] = '"'; + std::string_view cinput(&input, 1); + position += write_string_escaped(cinput, buffer.get() + position); + buffer.get()[position++] = '"'; + } +} + +simdjson_inline void +string_builder::escape_and_append_with_quotes(const char *input) noexcept { + std::string_view cinput(input); + escape_and_append_with_quotes(cinput); +} +#if SIMDJSON_SUPPORTS_CONCEPTS +template +simdjson_inline void string_builder::escape_and_append_with_quotes() noexcept { + escape_and_append_with_quotes(constevalutil::string_constant::value); +} +#endif + +simdjson_inline void string_builder::append_raw(const char *c) noexcept { + size_t len = std::strlen(c); + append_raw(c, len); +} + +simdjson_inline void +string_builder::append_raw(std::string_view input) noexcept { + if (capacity_check(input.size())) { + std::memcpy(buffer.get() + position, input.data(), input.size()); + position += input.size(); + } +} + +simdjson_inline void string_builder::append_raw(const char *str, + size_t len) noexcept { + if (capacity_check(len)) { + std::memcpy(buffer.get() + position, str, len); + position += len; + } +} +#if SIMDJSON_SUPPORTS_CONCEPTS +// Support for optional types (std::optional, etc.) +template + requires(!require_custom_serialization) +simdjson_inline void string_builder::append(const T &opt) { + if (opt) { + append(*opt); + } else { + append_null(); + } +} + +template + requires(require_custom_serialization) +simdjson_inline void string_builder::append(const T &val) { + serialize(*this, val); +} + +template + requires(std::is_convertible::value || + std::is_same::value) +simdjson_inline void string_builder::append(const T &value) { + escape_and_append_with_quotes(value); +} +#endif + +#if SIMDJSON_SUPPORTS_RANGES && SIMDJSON_SUPPORTS_CONCEPTS +// Support for range-based appending (std::ranges::view, etc.) +template + requires(!std::is_convertible::value) +simdjson_inline void string_builder::append(const R &range) noexcept { + auto it = std::ranges::begin(range); + auto end = std::ranges::end(range); + if constexpr (concepts::is_pair) { + start_object(); + + if (it == end) { + end_object(); + return; // Handle empty range + } + // Append first item without leading comma + append_key_value(it->first, it->second); + ++it; + + // Append remaining items with preceding commas + for (; it != end; ++it) { + append_comma(); + append_key_value(it->first, it->second); + } + end_object(); + } else { + start_array(); + if (it == end) { + end_array(); + return; // Handle empty range + } + + // Append first item without leading comma + append(*it); + ++it; + + // Append remaining items with preceding commas + for (; it != end; ++it) { + append_comma(); + append(*it); + } + end_array(); + } +} + +#endif + +#if SIMDJSON_EXCEPTIONS +simdjson_inline string_builder::operator std::string() const noexcept(false) { + return std::string(operator std::string_view()); +} + +simdjson_inline string_builder::operator std::string_view() const + noexcept(false) simdjson_lifetime_bound { + return view(); +} +#endif + +simdjson_inline simdjson_result +string_builder::view() const noexcept { + if (!is_valid) { + return simdjson::OUT_OF_CAPACITY; + } + return std::string_view(buffer.get(), position); +} + +simdjson_inline simdjson_result string_builder::c_str() noexcept { + if (capacity_check(1)) { + buffer.get()[position] = '\0'; + return buffer.get(); + } + return simdjson::OUT_OF_CAPACITY; +} + +simdjson_inline bool string_builder::validate_unicode() const noexcept { + return simdjson::validate_utf8(buffer.get(), position); +} + +simdjson_inline void string_builder::start_object() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = '{'; + } +} + +simdjson_inline void string_builder::end_object() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = '}'; + } +} + +simdjson_inline void string_builder::start_array() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = '['; + } +} + +simdjson_inline void string_builder::end_array() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = ']'; + } +} + +simdjson_inline void string_builder::append_comma() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = ','; + } +} + +simdjson_inline void string_builder::append_colon() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = ':'; + } +} + +template +simdjson_inline void +string_builder::append_key_value(key_type key, value_type value) noexcept { + static_assert(std::is_same::value || + std::is_convertible::value, + "Unsupported key type"); + escape_and_append_with_quotes(key); + append_colon(); + SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + append_null(); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR( + std::is_convertible::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else { + append(value); + } +} + +#if SIMDJSON_SUPPORTS_CONCEPTS +template +simdjson_inline void +string_builder::append_key_value(value_type value) noexcept { + escape_and_append_with_quotes(); + append_colon(); + SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + append_null(); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR( + std::is_convertible::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else { + append(value); + } +} +#endif + +} // namespace builder +} // namespace lasx +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_STRING_BUILDER_INL_H +/* end file simdjson/generic/ondemand/json_string_builder-inl.h for lasx */ +/* including simdjson/generic/ondemand/json_builder.h for lasx: #include "simdjson/generic/ondemand/json_builder.h" */ +/* begin file simdjson/generic/ondemand/json_builder.h for lasx */ +/** + * This file is part of the builder API. It is temporarily in the ondemand directory + * but we will move it to a builder directory later. + */ +#ifndef SIMDJSON_GENERIC_BUILDER_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_string_builder.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/concepts.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ +#if SIMDJSON_STATIC_REFLECTION + +#include +#include +#include +#include +#include +#include +#include +#include +// #include // for std::define_static_string - header not available yet + +namespace simdjson { +namespace lasx { +namespace builder { + +template + requires(concepts::container_but_not_string && !require_custom_serialization) +constexpr void atom(string_builder &b, const T &t) { + auto it = t.begin(); + auto end = t.end(); + if (it == end) { + b.append_raw("[]"); + return; + } + b.append('['); + atom(b, *it); + ++it; + for (; it != end; ++it) { + b.append(','); + atom(b, *it); + } + b.append(']'); +} + +template + requires(std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v) +constexpr void atom(string_builder &b, const T &t) { + b.escape_and_append_with_quotes(t); +} + +template + requires(!require_custom_serialization) +constexpr void atom(string_builder &b, const T &m) { + if (m.empty()) { + b.append_raw("{}"); + return; + } + b.append('{'); + bool first = true; + for (const auto& [key, value] : m) { + if (!first) { + b.append(','); + } + first = false; + // Keys must be convertible to string_view per the concept + b.escape_and_append_with_quotes(key); + b.append(':'); + atom(b, value); + } + b.append('}'); +} + + +template::value && !std::is_same_v>::type> +constexpr void atom(string_builder &b, const number_type t) { + b.append(t); +} + +template + requires(std::is_class_v && !concepts::container_but_not_string && + !concepts::string_view_keyed_map && + !concepts::optional_type && + !concepts::smart_pointer && + !concepts::appendable_containers && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && !require_custom_serialization) +constexpr void atom(string_builder &b, const T &t) { + int i = 0; + b.append('{'); + template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) { + if (i != 0) + b.append(','); + constexpr auto key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(dm))); + b.append_raw(key); + b.append(':'); + atom(b, t.[:dm:]); + i++; + }; + b.append('}'); +} + +// Support for optional types (std::optional, etc.) +template + requires(!require_custom_serialization) +constexpr void atom(string_builder &b, const T &opt) { + if (opt) { + atom(b, opt.value()); + } else { + b.append_raw("null"); + } +} + +// Support for smart pointers (std::unique_ptr, std::shared_ptr, etc.) +template + requires(!require_custom_serialization) +constexpr void atom(string_builder &b, const T &ptr) { + if (ptr) { + atom(b, *ptr); + } else { + b.append_raw("null"); + } +} + +// Support for enums - serialize as string representation using expand approach from P2996R12 +template + requires(std::is_enum_v && !require_custom_serialization) +void atom(string_builder &b, const T &e) { +#if SIMDJSON_STATIC_REFLECTION + constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T)); + template for (constexpr auto enum_val : enumerators) { + constexpr auto enum_str = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(enum_val))); + if (e == [:enum_val:]) { + b.append_raw(enum_str); + return; + } + }; + // Fallback to integer if enum value not found + atom(b, static_cast>(e)); +#else + // Fallback: serialize as integer if reflection not available + atom(b, static_cast>(e)); +#endif +} + +// Support for appendable containers that don't have operator[] (sets, etc.) +template + requires(!concepts::container_but_not_string && !concepts::string_view_keyed_map && + !concepts::optional_type && !concepts::smart_pointer && + !std::is_same_v && + !std::is_same_v && !std::is_same_v && !require_custom_serialization) +constexpr void atom(string_builder &b, const T &container) { + if (container.empty()) { + b.append_raw("[]"); + return; + } + b.append('['); + bool first = true; + for (const auto& item : container) { + if (!first) { + b.append(','); + } + first = false; + atom(b, item); + } + b.append(']'); +} + +// append functions that delegate to atom functions for primitive types +template + requires(std::is_arithmetic_v && !std::is_same_v) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!concepts::container_but_not_string && !concepts::string_view_keyed_map && + !concepts::optional_type && !concepts::smart_pointer && + !std::is_same_v && + !std::is_same_v && !std::is_same_v && !require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +// works for struct +template + requires(std::is_class_v && !concepts::container_but_not_string && + !concepts::string_view_keyed_map && + !concepts::optional_type && + !concepts::smart_pointer && + !concepts::appendable_containers && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && !require_custom_serialization) +void append(string_builder &b, const Z &z) { + int i = 0; + b.append('{'); + template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^Z, std::meta::access_context::unchecked()))) { + if (i != 0) + b.append(','); + constexpr auto key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(dm))); + b.append_raw(key); + b.append(':'); + atom(b, z.[:dm:]); + i++; + }; + b.append('}'); +} + +// works for container that have begin() and end() iterators +template + requires(concepts::container_but_not_string && !require_custom_serialization) +void append(string_builder &b, const Z &z) { + auto it = z.begin(); + auto end = z.end(); + if (it == end) { + b.append_raw("[]"); + return; + } + b.append('['); + atom(b, *it); + ++it; + for (; it != end; ++it) { + b.append(','); + atom(b, *it); + } + b.append(']'); +} + +template + requires (require_custom_serialization) +void append(string_builder &b, const Z &z) { + b.append(z); +} + + +template +simdjson_warn_unused simdjson_result to_json_string(const Z &z, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) { + string_builder b(initial_capacity); + append(b, z); + std::string_view s; + if(auto e = b.view().get(s); e) { return e; } + return std::string(s); +} + +template +simdjson_warn_unused simdjson_error to_json(const Z &z, std::string &s, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) { + string_builder b(initial_capacity); + append(b, z); + std::string_view view; + if(auto e = b.view().get(view); e) { return e; } + s.assign(view); + return SUCCESS; +} + +template +string_builder& operator<<(string_builder& b, const Z& z) { + append(b, z); + return b; +} + +// extract_from: Serialize only specific fields from a struct to JSON +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +void extract_from(string_builder &b, const T &obj) { + // Helper to check if a field name matches any of the requested fields + auto should_extract = [](std::string_view field_name) constexpr -> bool { + return ((FieldNames.view() == field_name) || ...); + }; + + b.append('{'); + bool first = true; + + // Iterate through all members of T using reflection + template for (constexpr auto mem : std::define_static_array( + std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) { + + if constexpr (std::meta::is_public(mem)) { + constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem)); + + // Only serialize this field if it's in our list of requested fields + if constexpr (should_extract(key)) { + if (!first) { + b.append(','); + } + first = false; + + // Serialize the key + constexpr auto quoted_key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(mem))); + b.append_raw(quoted_key); + b.append(':'); + + // Serialize the value + atom(b, obj.[:mem:]); + } + } + }; + + b.append('}'); +} + +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_result extract_from(const T &obj, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) { + string_builder b(initial_capacity); + extract_from(b, obj); + std::string_view s; + if(auto e = b.view().get(s); e) { return e; } + return std::string(s); +} + +} // namespace builder +} // namespace lasx +// Alias the function template to 'to' in the global namespace +template +simdjson_warn_unused simdjson_result to_json(const Z &z, size_t initial_capacity = lasx::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { + lasx::builder::string_builder b(initial_capacity); + lasx::builder::append(b, z); + std::string_view s; + if(auto e = b.view().get(s); e) { return e; } + return std::string(s); +} +template +simdjson_warn_unused simdjson_error to_json(const Z &z, std::string &s, size_t initial_capacity = lasx::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { + lasx::builder::string_builder b(initial_capacity); + lasx::builder::append(b, z); + std::string_view view; + if(auto e = b.view().get(view); e) { return e; } + s.assign(view); + return SUCCESS; +} +// Global namespace function for extract_from +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_result extract_from(const T &obj, size_t initial_capacity = lasx::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { + lasx::builder::string_builder b(initial_capacity); + lasx::builder::extract_from(b, obj); + std::string_view s; + if(auto e = b.view().get(s); e) { return e; } + return std::string(s); +} + +} // namespace simdjson + +#endif // SIMDJSON_STATIC_REFLECTION + +#endif +/* end file simdjson/generic/ondemand/json_builder.h for lasx */ /* end file simdjson/generic/ondemand/amalgamated.h for lasx */ /* including simdjson/lasx/end.h: #include "simdjson/lasx/end.h" */ @@ -123526,9 +142971,297 @@ namespace simdjson { * @copydoc simdjson::builtin::ondemand */ namespace ondemand = builtin::ondemand; + /** + * @copydoc simdjson::builtin::builder + */ + namespace builder = builtin::builder; + +#if SIMDJSON_STATIC_REFLECTION + /** + * Create a JSON string from any user-defined type using static reflection. + * Only available when SIMDJSON_STATIC_REFLECTION is enabled. + */ + template + requires(!std::same_as && + !std::same_as && + !std::same_as && + !std::same_as) + inline std::string to_json_string(const T& obj) { + builder::string_builder str_builder; + append(str_builder, obj); + std::string_view view; + if (str_builder.view().get(view) == SUCCESS) { + return std::string(view); + } + return ""; + } +#endif + } // namespace simdjson #endif // SIMDJSON_ONDEMAND_H /* end file simdjson/ondemand.h */ +/* including simdjson/convert.h: #include "simdjson/convert.h" */ +/* begin file simdjson/convert.h */ + +#ifndef SIMDJSON_CONVERT_H +#define SIMDJSON_CONVERT_H + +/* skipped duplicate #include "simdjson/ondemand.h" */ +#include + +#if SIMDJSON_SUPPORTS_CONCEPTS + + +namespace simdjson { +namespace convert { +namespace internal { + +/** + * A utility class for automatically parsing JSON documents. + * This template is NOT part of our public API. + * It is subject to changes. + * @private + */ +template +struct auto_parser { +private: + parser_type m_parser; + ondemand::document m_doc; + error_code m_error{SUCCESS}; + + template + static constexpr bool is_nothrow_gettable = requires(ondemand::document doc) { + { doc.get() } noexcept; + }; +public: + explicit auto_parser(parser_type &&parser, ondemand::document &&doc) noexcept requires(!std::is_pointer_v); + explicit auto_parser(parser_type &&parser, padded_string_view const str) noexcept requires(!std::is_pointer_v); + explicit auto_parser(std::remove_pointer_t &parser, ondemand::document &&doc) noexcept requires(std::is_pointer_v); + explicit auto_parser(std::remove_pointer_t &parser, padded_string_view const str) noexcept requires(std::is_pointer_v); + explicit auto_parser(padded_string_view const str) noexcept requires(std::is_pointer_v); + explicit auto_parser(parser_type parser, ondemand::document &&doc) noexcept requires(std::is_pointer_v); + auto_parser(auto_parser const &) = delete; + auto_parser &operator=(auto_parser const &) = delete; + auto_parser(auto_parser &&) noexcept = default; + auto_parser &operator=(auto_parser &&) noexcept = default; + ~auto_parser() = default; + + simdjson_warn_unused std::remove_pointer_t &parser() noexcept; + + template + simdjson_warn_unused simdjson_inline simdjson_result result() noexcept(is_nothrow_gettable); + template + simdjson_warn_unused simdjson_inline error_code get(T &value) && noexcept(is_nothrow_gettable); + + + simdjson_warn_unused simdjson_inline simdjson_result array() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result object() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result number() noexcept; + + +#if SIMDJSON_EXCEPTIONS + template + simdjson_warn_unused simdjson_inline explicit(false) operator T() noexcept(false); +#endif // SIMDJSON_EXCEPTIONS + + template + simdjson_warn_unused simdjson_inline std::optional optional() noexcept(is_nothrow_gettable); +}; + + +/** + * A utility class for adapting values for the `auto_parser`. + * This template is not part of our public API. It is subject to changes. + * @private + */ +template +struct to_adaptor { + T operator()(simdjson_result &val) const noexcept; + auto operator()(padded_string_view const str) const noexcept; + auto operator()(ondemand::parser &parser, padded_string_view const str) const noexcept; + // The std::string is padded with reserve to ensure there is enough space for padding. + // Some sanitizers may not like this, so you can use simdjson::pad instead. + // simdjson::from(simdjson::pad(str)) + auto operator()(std::string str) const noexcept; + auto operator()(ondemand::parser &parser, std::string str) const noexcept; +}; +// deduction guide +auto_parser(padded_string_view const str) -> auto_parser; +} // namespace internal +} // namespace convert + +/** + * The simdjson::from instance is EXPERIMENTAL AND SUBJECT TO CHANGES. + * + * The `from` instance is a utility adaptor for parsing JSON strings into objects. + * It provides a convenient way to convert JSON data into C++ objects using the `auto_parser`. + * + * Example usage: + * + * ```cpp + * std::map obj = + * simdjson::from(R"({"key": "value"})"_padded); + * ``` + * + * This will parse the JSON string and return an object representation. By default, we + * use the simdjson::ondemand::parser::get_parser() instance. A parser instance should + * be used for just one document at a time. + * + * You can also pass you own parser instance: + * ```cpp + * simdjson::ondemand::parser parser; + * std::map obj = + * simdjson::from(parser, R"({"key": "value"})"_padded); + * ``` + * The parser instance can be reused. + * + * This functionality requires C++20 or better. + */ +static constexpr convert::internal::to_adaptor<> from{}; + +} // namespace simdjson + +#endif // SIMDJSON_SUPPORTS_CONCEPTS +#endif // SIMDJSON_CONVERT_H +/* end file simdjson/convert.h */ +/* including simdjson/convert-inl.h: #include "simdjson/convert-inl.h" */ +/* begin file simdjson/convert-inl.h */ + +#ifndef SIMDJSON_CONVERT_INL_H +#define SIMDJSON_CONVERT_INL_H + +/* skipped duplicate #include "simdjson/convert.h" */ +#if SIMDJSON_SUPPORTS_CONCEPTS +namespace simdjson { +namespace convert { +namespace internal { +// auto_parser method definitions +template +inline auto_parser::auto_parser(parser_type &&parser, ondemand::document &&doc) noexcept requires(!std::is_pointer_v) + : m_parser{std::move(parser)}, m_doc{std::move(doc)} {} + +template +inline auto_parser::auto_parser(parser_type &&parser, padded_string_view const str) noexcept requires(!std::is_pointer_v) + : m_parser{std::move(parser)}, m_doc{}, m_error{SUCCESS} { + m_error = m_parser.iterate(str).get(m_doc); +} + +template +inline auto_parser::auto_parser(std::remove_pointer_t &parser, ondemand::document &&doc) noexcept requires(std::is_pointer_v) + : m_parser{&parser}, m_doc{std::move(doc)} {} + +template +inline auto_parser::auto_parser(std::remove_pointer_t &parser, padded_string_view const str) noexcept requires(std::is_pointer_v) + : m_parser{&parser}, m_doc{}, m_error{SUCCESS} { + m_error = m_parser->iterate(str).get(m_doc); +} + +template +inline auto_parser::auto_parser(padded_string_view const str) noexcept requires(std::is_pointer_v) + : auto_parser{ondemand::parser::get_parser(), str} {} + +template +inline auto_parser::auto_parser(parser_type parser, ondemand::document &&doc) noexcept requires(std::is_pointer_v) + : auto_parser{*parser, std::move(doc)} {} + + + + + +template +inline std::remove_pointer_t &auto_parser::parser() noexcept { + if constexpr (std::is_pointer_v) { + return *m_parser; + } else { + return m_parser; + } +} + +template +template +inline simdjson_result auto_parser::result() noexcept(is_nothrow_gettable) { + if (m_error != SUCCESS) { + return m_error; + } + return m_doc.get(); +} + +template +template +simdjson_warn_unused simdjson_inline error_code auto_parser::get(T &value) && noexcept(is_nothrow_gettable) { + return result().get(value); +} + +template +inline simdjson_result auto_parser::array() noexcept { + return result(); +} + +template +inline simdjson_result auto_parser::object() noexcept { + return result(); +} + +template +inline simdjson_result auto_parser::number() noexcept { + return result(); +} + +#if SIMDJSON_EXCEPTIONS +template +template +inline auto_parser::operator T() noexcept(false) { + if (m_error != SUCCESS) { + throw simdjson_error(m_error); + } + return m_doc.get(); +} +#endif // SIMDJSON_EXCEPTIONS + +template +template +inline std::optional auto_parser::optional() noexcept(is_nothrow_gettable) { + if (m_error != SUCCESS) { + return std::nullopt; + } + T value; + if (m_doc.get().get(value)) [[unlikely]] { + return std::nullopt; + } + return {std::move(value)}; +} + +// to_adaptor method definitions +template +inline T to_adaptor::operator()(simdjson_result &val) const noexcept { + return val.get(); +} + +template +inline auto to_adaptor::operator()(padded_string_view const str) const noexcept { + return auto_parser{str}; +} + +template +inline auto to_adaptor::operator()(ondemand::parser &parser, padded_string_view const str) const noexcept { + return auto_parser{parser, str}; +} + +template +inline auto to_adaptor::operator()(std::string str) const noexcept { + return auto_parser{pad_with_reserve(str)}; +} + +template +inline auto to_adaptor::operator()(ondemand::parser &parser, std::string str) const noexcept { + return auto_parser{parser, pad_with_reserve(str)}; +} +} // namespace internal +} // namespace convert +} // namespace simdjson +#endif // SIMDJSON_SUPPORTS_CONCEPTS +#endif // SIMDJSON_CONVERT_INL_H +/* end file simdjson/convert-inl.h */ #endif // SIMDJSON_H /* end file simdjson.h */ diff --git a/deps/v8/include/v8-script.h b/deps/v8/include/v8-script.h index 9d47b751f271eb..debe89b50847ea 100644 --- a/deps/v8/include/v8-script.h +++ b/deps/v8/include/v8-script.h @@ -220,6 +220,13 @@ class V8_EXPORT Module : public Data { Local context, Local specifier, Local import_attributes, Local referrer); + using ResolveModuleByIndexCallback = MaybeLocal (*)( + Local context, size_t module_request_index, + Local referrer); + using ResolveSourceByIndexCallback = MaybeLocal (*)( + Local context, size_t module_request_index, + Local referrer); + /** * Instantiates the module and its dependencies. * @@ -231,6 +238,16 @@ class V8_EXPORT Module : public Data { Local context, ResolveModuleCallback module_callback, ResolveSourceCallback source_callback = nullptr); + /** + * Similar to the variant that takes ResolveModuleCallback and + * ResolveSourceCallback, but uses the index into the array that is returned + * by GetModuleRequests() instead of the specifier and import attributes to + * identify the requests. + */ + V8_WARN_UNUSED_RESULT Maybe InstantiateModule( + Local context, ResolveModuleByIndexCallback module_callback, + ResolveSourceByIndexCallback source_callback = nullptr); + /** * Evaluates the module and its dependencies. * diff --git a/deps/v8/src/api/api.cc b/deps/v8/src/api/api.cc index e5f703bb5e7ddb..1ec43cb202a8a7 100644 --- a/deps/v8/src/api/api.cc +++ b/deps/v8/src/api/api.cc @@ -2266,14 +2266,34 @@ int Module::GetIdentityHash() const { return self->hash(); } +Maybe Module::InstantiateModule( + Local context, ResolveModuleByIndexCallback module_callback, + ResolveSourceByIndexCallback source_callback) { + auto i_isolate = i::Isolate::Current(); + EnterV8Scope<> api_scope{i_isolate, context, + RCCId::kAPI_Module_InstantiateModule}; + + i::Module::UserResolveCallbacks callbacks; + callbacks.module_callback_by_index = module_callback; + callbacks.source_callback_by_index = source_callback; + if (!i::Module::Instantiate(i_isolate, Utils::OpenHandle(this), context, + callbacks)) { + return {}; + } + return Just(true); +} + Maybe Module::InstantiateModule(Local context, ResolveModuleCallback module_callback, ResolveSourceCallback source_callback) { auto i_isolate = i::Isolate::Current(); EnterV8Scope<> api_scope{i_isolate, context, RCCId::kAPI_Module_InstantiateModule}; + i::Module::UserResolveCallbacks callbacks; + callbacks.module_callback = module_callback; + callbacks.source_callback = source_callback; if (!i::Module::Instantiate(i_isolate, Utils::OpenHandle(this), context, - module_callback, source_callback)) { + callbacks)) { return {}; } return Just(true); diff --git a/deps/v8/src/objects/module.cc b/deps/v8/src/objects/module.cc index 6ce67230b413a1..b232db1fcb3c97 100644 --- a/deps/v8/src/objects/module.cc +++ b/deps/v8/src/objects/module.cc @@ -205,14 +205,12 @@ MaybeHandle Module::ResolveExport(Isolate* isolate, Handle module, bool Module::Instantiate(Isolate* isolate, Handle module, v8::Local context, - v8::Module::ResolveModuleCallback module_callback, - v8::Module::ResolveSourceCallback source_callback) { + const Module::UserResolveCallbacks& callbacks) { #ifdef DEBUG PrintStatusMessage(*module, "Instantiating module "); #endif // DEBUG - if (!PrepareInstantiate(isolate, module, context, module_callback, - source_callback)) { + if (!PrepareInstantiate(isolate, module, context, callbacks)) { ResetGraph(isolate, module); DCHECK_EQ(module->status(), kUnlinked); return false; @@ -231,11 +229,9 @@ bool Module::Instantiate(Isolate* isolate, Handle module, return true; } -bool Module::PrepareInstantiate( - Isolate* isolate, DirectHandle module, - v8::Local context, - v8::Module::ResolveModuleCallback module_callback, - v8::Module::ResolveSourceCallback source_callback) { +bool Module::PrepareInstantiate(Isolate* isolate, DirectHandle module, + v8::Local context, + const UserResolveCallbacks& callbacks) { DCHECK_NE(module->status(), kEvaluating); DCHECK_NE(module->status(), kLinking); if (module->status() >= kPreLinking) return true; @@ -244,8 +240,7 @@ bool Module::PrepareInstantiate( if (IsSourceTextModule(*module)) { return SourceTextModule::PrepareInstantiate( - isolate, Cast(module), context, module_callback, - source_callback); + isolate, Cast(module), context, callbacks); } else { return SyntheticModule::PrepareInstantiate( isolate, Cast(module), context); diff --git a/deps/v8/src/objects/module.h b/deps/v8/src/objects/module.h index 7e6b3bb690a6fc..cab99ea314ba80 100644 --- a/deps/v8/src/objects/module.h +++ b/deps/v8/src/objects/module.h @@ -57,14 +57,20 @@ class Module : public TorqueGeneratedModule { // i.e. has a top-level await. V8_WARN_UNUSED_RESULT bool IsGraphAsync(Isolate* isolate) const; + struct UserResolveCallbacks { + v8::Module::ResolveModuleCallback module_callback = nullptr; + v8::Module::ResolveSourceCallback source_callback = nullptr; + v8::Module::ResolveModuleByIndexCallback module_callback_by_index = nullptr; + v8::Module::ResolveSourceByIndexCallback source_callback_by_index = nullptr; + }; + // Implementation of spec operation ModuleDeclarationInstantiation. // Returns false if an exception occurred during instantiation, true // otherwise. (In the case where the callback throws an exception, that // exception is propagated.) static V8_WARN_UNUSED_RESULT bool Instantiate( Isolate* isolate, Handle module, v8::Local context, - v8::Module::ResolveModuleCallback module_callback, - v8::Module::ResolveSourceCallback source_callback); + const UserResolveCallbacks& callbacks); // Implementation of spec operation ModuleEvaluation. static V8_WARN_UNUSED_RESULT MaybeDirectHandle Evaluate( @@ -99,9 +105,7 @@ class Module : public TorqueGeneratedModule { static V8_WARN_UNUSED_RESULT bool PrepareInstantiate( Isolate* isolate, DirectHandle module, - v8::Local context, - v8::Module::ResolveModuleCallback module_callback, - v8::Module::ResolveSourceCallback source_callback); + v8::Local context, const UserResolveCallbacks& callbacks); static V8_WARN_UNUSED_RESULT bool FinishInstantiate( Isolate* isolate, Handle module, ZoneForwardList>* stack, unsigned* dfs_index, diff --git a/deps/v8/src/objects/source-text-module.cc b/deps/v8/src/objects/source-text-module.cc index 1237c90a1e273a..7e87cd010b0ebb 100644 --- a/deps/v8/src/objects/source-text-module.cc +++ b/deps/v8/src/objects/source-text-module.cc @@ -346,9 +346,10 @@ MaybeHandle SourceTextModule::ResolveExportUsingStarExports( bool SourceTextModule::PrepareInstantiate( Isolate* isolate, DirectHandle module, v8::Local context, - v8::Module::ResolveModuleCallback module_callback, - v8::Module::ResolveSourceCallback source_callback) { - DCHECK_NE(module_callback, nullptr); + const Module::UserResolveCallbacks& callbacks) { + // One of the callbacks must be set, otherwise we cannot resolve. + DCHECK_IMPLIES(callbacks.module_callback == nullptr, + callbacks.module_callback_by_index != nullptr); // Obtain requested modules. DirectHandle module_info(module->info(), isolate); DirectHandle module_requests(module_info->module_requests(), @@ -364,11 +365,23 @@ bool SourceTextModule::PrepareInstantiate( switch (module_request->phase()) { case ModuleImportPhase::kEvaluation: { v8::Local api_requested_module; - if (!module_callback(context, v8::Utils::ToLocal(specifier), - v8::Utils::FixedArrayToLocal(import_attributes), - v8::Utils::ToLocal(Cast(module))) - .ToLocal(&api_requested_module)) { - return false; + if (callbacks.module_callback != nullptr) { + if (!callbacks + .module_callback( + context, v8::Utils::ToLocal(specifier), + v8::Utils::FixedArrayToLocal(import_attributes), + v8::Utils::ToLocal(Cast(module))) + .ToLocal(&api_requested_module)) { + return false; + } + } else { + DCHECK_NOT_NULL(callbacks.module_callback_by_index); + if (!callbacks + .module_callback_by_index( + context, i, v8::Utils::ToLocal(Cast(module))) + .ToLocal(&api_requested_module)) { + return false; + } } DirectHandle requested_module = Utils::OpenDirectHandle(*api_requested_module); @@ -378,11 +391,23 @@ bool SourceTextModule::PrepareInstantiate( case ModuleImportPhase::kSource: { DCHECK(v8_flags.js_source_phase_imports); v8::Local api_requested_module_source; - if (!source_callback(context, v8::Utils::ToLocal(specifier), - v8::Utils::FixedArrayToLocal(import_attributes), - v8::Utils::ToLocal(Cast(module))) - .ToLocal(&api_requested_module_source)) { - return false; + if (callbacks.source_callback != nullptr) { + if (!callbacks + .source_callback( + context, v8::Utils::ToLocal(specifier), + v8::Utils::FixedArrayToLocal(import_attributes), + v8::Utils::ToLocal(Cast(module))) + .ToLocal(&api_requested_module_source)) { + return false; + } + } else { + DCHECK_NOT_NULL(callbacks.source_callback_by_index); + if (!callbacks + .source_callback_by_index( + context, i, v8::Utils::ToLocal(Cast(module))) + .ToLocal(&api_requested_module_source)) { + return false; + } } DirectHandle requested_module_source = Utils::OpenDirectHandle(*api_requested_module_source); @@ -404,7 +429,7 @@ bool SourceTextModule::PrepareInstantiate( DirectHandle requested_module( Cast(requested_modules->get(i)), isolate); if (!Module::PrepareInstantiate(isolate, requested_module, context, - module_callback, source_callback)) { + callbacks)) { return false; } } diff --git a/deps/v8/src/objects/source-text-module.h b/deps/v8/src/objects/source-text-module.h index 05b4576bfa4a69..e74de1ecfc1cd1 100644 --- a/deps/v8/src/objects/source-text-module.h +++ b/deps/v8/src/objects/source-text-module.h @@ -172,8 +172,7 @@ class SourceTextModule static V8_WARN_UNUSED_RESULT bool PrepareInstantiate( Isolate* isolate, DirectHandle module, v8::Local context, - v8::Module::ResolveModuleCallback module_callback, - v8::Module::ResolveSourceCallback source_callback); + const Module::UserResolveCallbacks& callbacks); static V8_WARN_UNUSED_RESULT bool FinishInstantiate( Isolate* isolate, Handle module, ZoneForwardList>* stack, unsigned* dfs_index, diff --git a/deps/v8/test/unittests/objects/modules-unittest.cc b/deps/v8/test/unittests/objects/modules-unittest.cc index 08663f975e6468..07a6fee32b63a2 100644 --- a/deps/v8/test/unittests/objects/modules-unittest.cc +++ b/deps/v8/test/unittests/objects/modules-unittest.cc @@ -6,6 +6,9 @@ #include "src/flags/flags.h" #include "test/unittests/test-utils.h" #include "testing/gtest/include/gtest/gtest.h" +#if V8_ENABLE_WEBASSEMBLY +#include "include/v8-wasm.h" +#endif // V8_ENABLE_WEBASSEMBLY namespace { @@ -1259,4 +1262,293 @@ TEST_F(ModuleTest, AsyncEvaluatingInEvaluateEntryPoint) { CHECK_EQ(v8::Promise::kFulfilled, promise1->State()); } +// Test data for index-based module resolution +static std::vector> index_modules_global; + +static MaybeLocal ResolveModuleByIndexCallback( + Local context, size_t module_request_index, + Local referrer) { + Isolate* isolate = Isolate::GetCurrent(); + CHECK_LE(module_request_index, index_modules_global.size()); + return index_modules_global[module_request_index].Get(isolate); +} + +static MaybeLocal ResolveSourceByIndexUnreachableCallback( + Local context, size_t module_request_index, + Local referrer) { + UNREACHABLE(); +} + +TEST_F(ModuleTest, ModuleInstantiationByIndex) { + HandleScope scope(isolate()); + v8::TryCatch try_catch(isolate()); + + Local module; + { + Local source_text = NewString( + "import { x } from './dep1.js';\n" + "export { y } from './dep2.js';\n" + "export const z = x;\n"); + ScriptOrigin origin = ModuleOrigin(NewString("main.js"), isolate()); + ScriptCompiler::Source source(source_text, origin); + module = ScriptCompiler::CompileModule(isolate(), &source).ToLocalChecked(); + CHECK_EQ(Module::kUninstantiated, module->GetStatus()); + + Local module_requests = module->GetModuleRequests(); + CHECK_EQ(2, module_requests->Length()); + + // Verify the requests are in expected order + Local module_request_0 = + module_requests->Get(context(), 0).As(); + CHECK( + NewString("./dep1.js")->StrictEquals(module_request_0->GetSpecifier())); + + Local module_request_1 = + module_requests->Get(context(), 1).As(); + CHECK( + NewString("./dep2.js")->StrictEquals(module_request_1->GetSpecifier())); + } + + // Create dependency modules to be resolved by index + { + Local source_text = NewString("export const x = 42;"); + ScriptOrigin origin = ModuleOrigin(NewString("dep1.js"), isolate()); + ScriptCompiler::Source source(source_text, origin); + Local dep1 = + ScriptCompiler::CompileModule(isolate(), &source).ToLocalChecked(); + index_modules_global.emplace_back(isolate(), dep1); + } + + { + Local source_text = NewString("export const y = 24;"); + ScriptOrigin origin = ModuleOrigin(NewString("dep2.js"), isolate()); + ScriptCompiler::Source source(source_text, origin); + Local dep2 = + ScriptCompiler::CompileModule(isolate(), &source).ToLocalChecked(); + index_modules_global.emplace_back(isolate(), dep2); + } + + // Instantiate using index-based callback + CHECK(module + ->InstantiateModule(context(), ResolveModuleByIndexCallback, + ResolveSourceByIndexUnreachableCallback) + .FromJust()); + CHECK_EQ(Module::kInstantiated, module->GetStatus()); + + // Verify evaluation works + MaybeLocal result = module->Evaluate(context()); + CHECK_EQ(Module::kEvaluated, module->GetStatus()); + Local promise = Local::Cast(result.ToLocalChecked()); + CHECK_EQ(promise->State(), v8::Promise::kFulfilled); + + CHECK(!try_catch.HasCaught()); + + CHECK_EQ(42, module->GetModuleNamespace() + .As() + ->Get(context(), NewString("z")) + .ToLocalChecked() + ->Int32Value(context()) + .ToChecked()); + CHECK_EQ(24, module->GetModuleNamespace() + .As() + ->Get(context(), NewString("y")) + .ToLocalChecked() + ->Int32Value(context()) + .ToChecked()); + // Clean up + for (auto& mod : index_modules_global) { + mod.Reset(); + } + index_modules_global.clear(); +} + +static bool resolve_module_by_index_failure_called = false; +static MaybeLocal ResolveModuleByIndexFailureCallback( + Local context, size_t module_request_index, + Local referrer) { + CHECK(!resolve_module_by_index_failure_called); + resolve_module_by_index_failure_called = true; + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + std::string message = + "module " + std::to_string(module_request_index) + " not found"; + isolate->ThrowException( + String::NewFromUtf8(isolate, message.c_str()).ToLocalChecked()); + return MaybeLocal(); +} + +TEST_F(ModuleTest, ModuleInstantiationByIndexFailure) { + HandleScope scope(isolate()); + v8::TryCatch try_catch(isolate()); + + Local module; + { + Local source_text = NewString( + "import './dep1.js';\n" + "import './dep2.js';\n" + "import './dep3.js';"); + ScriptOrigin origin = ModuleOrigin(NewString("main.js"), isolate()); + ScriptCompiler::Source source(source_text, origin); + module = ScriptCompiler::CompileModule(isolate(), &source).ToLocalChecked(); + CHECK_EQ(Module::kUninstantiated, module->GetStatus()); + + Local module_requests = module->GetModuleRequests(); + CHECK_EQ(3, module_requests->Length()); + } + + // Instantiation should fail and the callback should only be called once. + { + v8::TryCatch inner_try_catch(isolate()); + CHECK(module + ->InstantiateModule(context(), + ResolveModuleByIndexFailureCallback, + ResolveSourceByIndexUnreachableCallback) + .IsNothing()); + CHECK(inner_try_catch.HasCaught()); + CHECK(inner_try_catch.Exception()->StrictEquals( + NewString("module 0 not found"))); + CHECK_EQ(Module::kUninstantiated, module->GetStatus()); + } + + // Should not leak to the outer try-catch. + CHECK(!try_catch.HasCaught()); +} + +static bool resolve_source_by_index_failure_called = false; +MaybeLocal ResolveSourceByIndexFailureCallback( + Local context, size_t module_request_index, + Local referrer) { + CHECK(!resolve_source_by_index_failure_called); + resolve_source_by_index_failure_called = true; + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + std::string message = + "module source " + std::to_string(module_request_index) + " not found"; + isolate->ThrowException( + String::NewFromUtf8(isolate, message.c_str()).ToLocalChecked()); + return MaybeLocal(); +} + +static MaybeLocal ResolveModuleByIndexUnreachableCallback( + Local context, size_t module_request_index, + Local referrer) { + UNREACHABLE(); +} + +TEST_F(ModuleTest, ModuleInstantiationByIndexWithSourceFaliure) { + bool prev_import_attributes = i::v8_flags.js_source_phase_imports; + i::v8_flags.js_source_phase_imports = true; + HandleScope scope(isolate()); + v8::TryCatch try_catch(isolate()); + + Local module; + { + Local source_text = NewString( + "import source mod from './foo.wasm;'\n" + "export { mod };\n"); + ScriptOrigin origin = ModuleOrigin(NewString("main.js"), isolate()); + ScriptCompiler::Source source(source_text, origin); + module = ScriptCompiler::CompileModule(isolate(), &source).ToLocalChecked(); + CHECK_EQ(Module::kUninstantiated, module->GetStatus()); + + Local module_requests = module->GetModuleRequests(); + CHECK_EQ(1, module_requests->Length()); + } + + // Instantiation should fail and the callback should only be called once. + { + v8::TryCatch inner_try_catch(isolate()); + CHECK(module + ->InstantiateModule(context(), + ResolveModuleByIndexUnreachableCallback, + ResolveSourceByIndexFailureCallback) + .IsNothing()); + CHECK(inner_try_catch.HasCaught()); + CHECK(inner_try_catch.Exception()->StrictEquals( + NewString("module source 0 not found"))); + CHECK_EQ(Module::kUninstantiated, module->GetStatus()); + } + + // Should not leak to the outer try-catch. + CHECK(!try_catch.HasCaught()); + i::v8_flags.js_source_phase_imports = prev_import_attributes; +} + +#if V8_ENABLE_WEBASSEMBLY + +// The bytes of a minimal WebAssembly module. +static const uint8_t kMinimalWasmModuleBytes[]{0x00, 0x61, 0x73, 0x6d, + 0x01, 0x00, 0x00, 0x00}; + +static bool resolve_source_by_index_called = false; +static v8::Global wasm_module_global; +MaybeLocal ResolveSourceByIndexCallback(Local context, + size_t module_request_index, + Local referrer) { + CHECK(!resolve_source_by_index_called); + resolve_source_by_index_called = true; + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + return wasm_module_global.Get(isolate); +} + +TEST_F(ModuleTest, ModuleInstantiationByIndexWithSource) { + bool prev_import_attributes = i::v8_flags.js_source_phase_imports; + i::v8_flags.js_source_phase_imports = true; + HandleScope scope(isolate()); + v8::TryCatch try_catch(isolate()); + + Local module; + { + Local source_text = NewString( + "import source mod from './foo.wasm';\n" + "export { mod };\n"); + ScriptOrigin origin = ModuleOrigin(NewString("main.js"), isolate()); + ScriptCompiler::Source source(source_text, origin); + module = ScriptCompiler::CompileModule(isolate(), &source).ToLocalChecked(); + CHECK_EQ(Module::kUninstantiated, module->GetStatus()); + + Local module_requests = module->GetModuleRequests(); + CHECK_EQ(1, module_requests->Length()); + + Local module_request_0 = + module_requests->Get(context(), 0).As(); + CHECK(NewString("./foo.wasm") + ->StrictEquals(module_request_0->GetSpecifier())); + CHECK_EQ(v8::ModuleImportPhase::kSource, module_request_0->GetPhase()); + } + + { + Local wasm_module = + v8::WasmModuleObject::Compile( + isolate(), + {kMinimalWasmModuleBytes, arraysize(kMinimalWasmModuleBytes)}) + .ToLocalChecked(); + wasm_module_global.Reset(isolate(), wasm_module); + } + + // Instantiate using index-based callback + CHECK(module + ->InstantiateModule(context(), + ResolveModuleByIndexUnreachableCallback, + ResolveSourceByIndexCallback) + .FromJust()); + CHECK_EQ(Module::kInstantiated, module->GetStatus()); + + // Verify evaluation works + MaybeLocal result = module->Evaluate(context()); + CHECK_EQ(Module::kEvaluated, module->GetStatus()); + Local promise = Local::Cast(result.ToLocalChecked()); + CHECK_EQ(promise->State(), v8::Promise::kFulfilled); + + CHECK(!try_catch.HasCaught()); + Local mod = module->GetModuleNamespace() + .As() + ->Get(context(), NewString("mod")) + .ToLocalChecked(); + CHECK(mod->StrictEquals(wasm_module_global.Get(isolate()))); + + i::v8_flags.js_source_phase_imports = prev_import_attributes; + wasm_module_global.Reset(); +} + +#endif // V8_ENABLE_WEBASSEMBLY + } // anonymous namespace diff --git a/doc/api/assert.md b/doc/api/assert.md index 32b462fbfc16d7..0358e630526a15 100644 --- a/doc/api/assert.md +++ b/doc/api/assert.md @@ -317,6 +317,9 @@ An alias of [`assert.ok()`][]. -> Stability: 1.0 - Early development. Enable this API with the -> \[`--experimental-webstorage`]\[] CLI flag. +> Stability: 1.0 - Early development. A browser-compatible implementation of {Storage}. Disable this API with the [`--no-webstorage`][] (or its alias `--no-experimental-webstorage`) CLI flag. diff --git a/doc/api/http.md b/doc/api/http.md index b11f5a974d635d..d434d9ee7b36a4 100644 --- a/doc/api/http.md +++ b/doc/api/http.md @@ -3555,6 +3555,9 @@ Found'`. -> Stability: 1.2 - Release candidate (asynchronous version) -> Stability: 1.1 - Active development (synchronous version) +> Stability: 1.1 - Active development There are two types of module customization hooks that are currently supported: diff --git a/doc/api/sqlite.md b/doc/api/sqlite.md index bb77b77f5bdb78..4cd9bb29ac5822 100644 --- a/doc/api/sqlite.md +++ b/doc/api/sqlite.md @@ -98,6 +98,10 @@ exposed by this class execute synchronously. + +* `active` {boolean} Whether to set the defensive flag. + +Enables or disables the defensive flag. When the defensive flag is active, +language features that allow ordinary SQL to deliberately corrupt the database file are disabled. +See [`SQLITE_DBCONFIG_DEFENSIVE`][] in the SQLite documentation for details. + ### `database.location([dbName])` -* `err` {Error} +* `err` {any} The `'error'` event is emitted if the worker thread throws an uncaught exception. In that case, the worker is terminated. diff --git a/doc/changelogs/CHANGELOG_V25.md b/doc/changelogs/CHANGELOG_V25.md index 4e9201ab1d8e90..1e9d018a4cc417 100644 --- a/doc/changelogs/CHANGELOG_V25.md +++ b/doc/changelogs/CHANGELOG_V25.md @@ -8,6 +8,7 @@ +25.1.0
25.0.0
@@ -40,6 +41,101 @@ * [io.js](CHANGELOG_IOJS.md) * [Archive](CHANGELOG_ARCHIVE.md) + + +## 2025-10-28, Version 25.1.0 (Current), @aduh95 + +### Notable Changes + +* \[[`4395fe14b9`](https://github.com/nodejs/node/commit/4395fe14b9)] - **(SEMVER-MINOR)** **http**: add optimizeEmptyRequests server option (Rafael Gonzaga) [#59778](https://github.com/nodejs/node/pull/59778) +* \[[`2e55c6ad04`](https://github.com/nodejs/node/commit/2e55c6ad04)] - **(SEMVER-MINOR)** **sqlite**: allow setting defensive flag (Bart Louwers) [#60217](https://github.com/nodejs/node/pull/60217) +* \[[`f437204491`](https://github.com/nodejs/node/commit/f437204491)] - **(SEMVER-MINOR)** **src**: add watch config namespace (Marco Ippolito) [#60178](https://github.com/nodejs/node/pull/60178) + +### Commits + +* \[[`bb27766bd5`](https://github.com/nodejs/node/commit/bb27766bd5)] - **benchmark**: improve cpu.sh for safety and usability (Nam Yooseong) [#60162](https://github.com/nodejs/node/pull/60162) +* \[[`e600711c20`](https://github.com/nodejs/node/commit/e600711c20)] - **benchmark**: add benchmark for leaf source text modules (Joyee Cheung) [#60205](https://github.com/nodejs/node/pull/60205) +* \[[`1bbcdf9039`](https://github.com/nodejs/node/commit/1bbcdf9039)] - **benchmark**: add vm.SourceTextModule benchmark (Joyee Cheung) [#59396](https://github.com/nodejs/node/pull/59396) +* \[[`22fa6bd28b`](https://github.com/nodejs/node/commit/22fa6bd28b)] - **build**: ibmi follow aix visibility (SRAVANI GUNDEPALLI) [#60360](https://github.com/nodejs/node/pull/60360) +* \[[`931028400e`](https://github.com/nodejs/node/commit/931028400e)] - **build**: use call command when calling python configure (Jacob Nichols) [#60098](https://github.com/nodejs/node/pull/60098) +* \[[`17fde3f3d1`](https://github.com/nodejs/node/commit/17fde3f3d1)] - **build**: build v8 with -fvisibility=hidden -fvisibility-inlines-hidden (Joyee Cheung) [#56290](https://github.com/nodejs/node/pull/56290) +* \[[`04cc7aae5e`](https://github.com/nodejs/node/commit/04cc7aae5e)] - **build**: remove V8\_COMPRESS\_POINTERS\_IN\_ISOLATE\_CAGE defs (Joyee Cheung) [#60296](https://github.com/nodejs/node/pull/60296) +* \[[`8a2053060d`](https://github.com/nodejs/node/commit/8a2053060d)] - **crypto**: update root certificates to NSS 3.116 (Node.js GitHub Bot) [#59956](https://github.com/nodejs/node/pull/59956) +* \[[`fe91c0f755`](https://github.com/nodejs/node/commit/fe91c0f755)] - **deps**: update simdjson to 4.0.7 (Node.js GitHub Bot) [#59883](https://github.com/nodejs/node/pull/59883) +* \[[`aacfc0d212`](https://github.com/nodejs/node/commit/aacfc0d212)] - **deps**: update corepack to 0.34.1 (Node.js GitHub Bot) [#60314](https://github.com/nodejs/node/pull/60314) +* \[[`8596891a71`](https://github.com/nodejs/node/commit/8596891a71)] - **deps**: update inspector\_protocol to af7f5a8173fdbc29f0835ec94395932e328b (Node.js GitHub Bot) [#60312](https://github.com/nodejs/node/pull/60312) +* \[[`21bcd0eb2f`](https://github.com/nodejs/node/commit/21bcd0eb2f)] - **deps**: V8: cherry-pick 3d0f462a17ff (Joyee Cheung) [#59396](https://github.com/nodejs/node/pull/59396) +* \[[`673558501c`](https://github.com/nodejs/node/commit/673558501c)] - **deps**: update googletest to 279f847 (Node.js GitHub Bot) [#60219](https://github.com/nodejs/node/pull/60219) +* \[[`425a1879b1`](https://github.com/nodejs/node/commit/425a1879b1)] - **doc**: mention more codemods in `deprecations.md` (Augustin Mauroy) [#60243](https://github.com/nodejs/node/pull/60243) +* \[[`563e1317f3`](https://github.com/nodejs/node/commit/563e1317f3)] - **doc**: remove unnecessary statement of web storage (Deokjin Kim) [#60363](https://github.com/nodejs/node/pull/60363) +* \[[`064c8c5cfd`](https://github.com/nodejs/node/commit/064c8c5cfd)] - **doc**: add missing CAA type to dns.resolveAny() & dnsPromises.resolveAny() (Jimmy Leung) [#58899](https://github.com/nodejs/node/pull/58899) +* \[[`99e357af35`](https://github.com/nodejs/node/commit/99e357af35)] - **doc**: use `any` for `worker_threads.Worker` 'error' event argument `err` (Jonas Geiler) [#60300](https://github.com/nodejs/node/pull/60300) +* \[[`8ccff0d934`](https://github.com/nodejs/node/commit/8ccff0d934)] - **doc**: update decorator documentation to reflect actual policy (Muhammad Salman Aziz) [#60288](https://github.com/nodejs/node/pull/60288) +* \[[`bac70c6ef3`](https://github.com/nodejs/node/commit/bac70c6ef3)] - **doc**: document wildcard supported by tools/test.py (Joyee Cheung) [#60265](https://github.com/nodejs/node/pull/60265) +* \[[`8492bc6a88`](https://github.com/nodejs/node/commit/8492bc6a88)] - **doc**: add --heap-snapshot-on-oom to useful v8 flag (jakecastelli) [#60260](https://github.com/nodejs/node/pull/60260) +* \[[`0f0d3c0e47`](https://github.com/nodejs/node/commit/0f0d3c0e47)] - **doc**: fix `blob.bytes()` heading level (XTY) [#60252](https://github.com/nodejs/node/pull/60252) +* \[[`8c8525cf93`](https://github.com/nodejs/node/commit/8c8525cf93)] - **doc**: fix not working code example in vm docs (Artur Gawlik) [#60224](https://github.com/nodejs/node/pull/60224) +* \[[`8a6de3866c`](https://github.com/nodejs/node/commit/8a6de3866c)] - **doc, assert**: correct order of changes entries (Gerhard Stöbich) [#60304](https://github.com/nodejs/node/pull/60304) +* \[[`6bacb6555a`](https://github.com/nodejs/node/commit/6bacb6555a)] - **doc, module**: change async customization hooks to experimental (Gerhard Stöbich) [#60302](https://github.com/nodejs/node/pull/60302) +* \[[`6f3b16df16`](https://github.com/nodejs/node/commit/6f3b16df16)] - **esm**: use index-based resolution callbacks (Joyee Cheung) [#59396](https://github.com/nodejs/node/pull/59396) +* \[[`95644a432c`](https://github.com/nodejs/node/commit/95644a432c)] - **http**: lazy allocate cookies array (Robert Nagy) [#59734](https://github.com/nodejs/node/pull/59734) +* \[[`4395fe14b9`](https://github.com/nodejs/node/commit/4395fe14b9)] - **(SEMVER-MINOR)** **http**: add optimizeEmptyRequests server option (Rafael Gonzaga) [#59778](https://github.com/nodejs/node/pull/59778) +* \[[`f1aa1eaaf5`](https://github.com/nodejs/node/commit/f1aa1eaaf5)] - **inspector**: add network payload buffer size limits (Chengzhong Wu) [#60236](https://github.com/nodejs/node/pull/60236) +* \[[`64fc625bf9`](https://github.com/nodejs/node/commit/64fc625bf9)] - **inspector**: support handshake response for websocket inspection (Shima Ryuhei) [#60225](https://github.com/nodejs/node/pull/60225) +* \[[`0ecbb806a8`](https://github.com/nodejs/node/commit/0ecbb806a8)] - **lib**: fix typo in createBlobReaderStream (SeokHun) [#60132](https://github.com/nodejs/node/pull/60132) +* \[[`ffec5927fd`](https://github.com/nodejs/node/commit/ffec5927fd)] - **meta**: fix typo in test-shared workflow name (Ronit Sabhaya) [#60321](https://github.com/nodejs/node/pull/60321) +* \[[`a02897e157`](https://github.com/nodejs/node/commit/a02897e157)] - **meta**: move one or more collaborators to emeritus (Node.js GitHub Bot) [#60325](https://github.com/nodejs/node/pull/60325) +* \[[`59223a7831`](https://github.com/nodejs/node/commit/59223a7831)] - **meta**: loop userland-migrations in deprecations (Chengzhong Wu) [#60299](https://github.com/nodejs/node/pull/60299) +* \[[`2d48d17696`](https://github.com/nodejs/node/commit/2d48d17696)] - **module**: refactor and clarify async loader hook customizations (Joyee Cheung) [#60278](https://github.com/nodejs/node/pull/60278) +* \[[`be1b84fd93`](https://github.com/nodejs/node/commit/be1b84fd93)] - **module**: handle null source from async loader hooks in sync hooks (Joyee Cheung) [#59929](https://github.com/nodejs/node/pull/59929) +* \[[`063fbd87d3`](https://github.com/nodejs/node/commit/063fbd87d3)] - **msi**: fix WiX warnings (Stefan Stojanovic) [#60251](https://github.com/nodejs/node/pull/60251) +* \[[`2e55c6ad04`](https://github.com/nodejs/node/commit/2e55c6ad04)] - **(SEMVER-MINOR)** **sqlite**: allow setting defensive flag (Bart Louwers) [#60217](https://github.com/nodejs/node/pull/60217) +* \[[`dc93d6988a`](https://github.com/nodejs/node/commit/dc93d6988a)] - **src**: fix timing of snapshot serialize callback (Joyee Cheung) [#60434](https://github.com/nodejs/node/pull/60434) +* \[[`267e1b3817`](https://github.com/nodejs/node/commit/267e1b3817)] - **src**: add COUNT\_GENERIC\_USAGE utility for tests (Joyee Cheung) [#60434](https://github.com/nodejs/node/pull/60434) +* \[[`4a5d7a4c2a`](https://github.com/nodejs/node/commit/4a5d7a4c2a)] - **src**: conditionally disable source phase imports by default (Shelley Vohr) [#60364](https://github.com/nodejs/node/pull/60364) +* \[[`f437204491`](https://github.com/nodejs/node/commit/f437204491)] - **(SEMVER-MINOR)** **src**: add watch config namespace (Marco Ippolito) [#60178](https://github.com/nodejs/node/pull/60178) +* \[[`36837fa0f9`](https://github.com/nodejs/node/commit/36837fa0f9)] - **src**: use cached primordials\_string (Sohyeon Kim) [#60255](https://github.com/nodejs/node/pull/60255) +* \[[`df8396ad37`](https://github.com/nodejs/node/commit/df8396ad37)] - **src**: replace Environment::GetCurrent with args.GetIsolate (Sohyeon Kim) [#60256](https://github.com/nodejs/node/pull/60256) +* \[[`5dd670b2b9`](https://github.com/nodejs/node/commit/5dd670b2b9)] - **src**: initial enablement of IsolateGroups (James M Snell) [#60254](https://github.com/nodejs/node/pull/60254) +* \[[`afdb362933`](https://github.com/nodejs/node/commit/afdb362933)] - **src**: use `Utf8Value` and `TwoByteValue` instead of V8 helpers (Anna Henningsen) [#60244](https://github.com/nodejs/node/pull/60244) +* \[[`a40e533e72`](https://github.com/nodejs/node/commit/a40e533e72)] - **src**: add a default branch for module phase (Chengzhong Wu) [#60261](https://github.com/nodejs/node/pull/60261) +* \[[`42729f07ee`](https://github.com/nodejs/node/commit/42729f07ee)] - **src**: stop using deprecated v8::Context::GetIsolate (Michaël Zasso) [#60223](https://github.com/nodejs/node/pull/60223) +* \[[`7a6542c205`](https://github.com/nodejs/node/commit/7a6542c205)] - **test**: skip failing test on macOS 15.7+ (Antoine du Hamel) [#60419](https://github.com/nodejs/node/pull/60419) +* \[[`29a5855a4f`](https://github.com/nodejs/node/commit/29a5855a4f)] - **test**: ensure assertions are reachable in `test/addons` (Antoine du Hamel) [#60142](https://github.com/nodejs/node/pull/60142) +* \[[`12773d19c4`](https://github.com/nodejs/node/commit/12773d19c4)] - **test**: increase debugger waitFor timeout on macOS (Chengzhong Wu) [#60367](https://github.com/nodejs/node/pull/60367) +* \[[`0b38de3e9e`](https://github.com/nodejs/node/commit/0b38de3e9e)] - **test**: put helper in test-runner-output into common (Joyee Cheung) [#60330](https://github.com/nodejs/node/pull/60330) +* \[[`6de2407c44`](https://github.com/nodejs/node/commit/6de2407c44)] - **test**: fix small compile warning in test\_network\_requests\_buffer.cc (xiaocainiao633) [#60281](https://github.com/nodejs/node/pull/60281) +* \[[`4b23ac8613`](https://github.com/nodejs/node/commit/4b23ac8613)] - **test**: fix status when compiled without inspector (Antoine du Hamel) [#60289](https://github.com/nodejs/node/pull/60289) +* \[[`a07f32e326`](https://github.com/nodejs/node/commit/a07f32e326)] - **test**: split test-runner-watch-mode-kill-signal (Joyee Cheung) [#60298](https://github.com/nodejs/node/pull/60298) +* \[[`30451d32d7`](https://github.com/nodejs/node/commit/30451d32d7)] - **test**: fix incorrect calculation in test-perf-hooks.js (Joyee Cheung) [#60271](https://github.com/nodejs/node/pull/60271) +* \[[`e3c3b48f1c`](https://github.com/nodejs/node/commit/e3c3b48f1c)] - **test**: ignore EPIPE errors in https proxy invalid URL test (Joyee Cheung) [#60269](https://github.com/nodejs/node/pull/60269) +* \[[`405a9c4c5f`](https://github.com/nodejs/node/commit/405a9c4c5f)] - **test**: parallelize test-without-async-context-frame correctly (Joyee Cheung) [#60273](https://github.com/nodejs/node/pull/60273) +* \[[`ffeebebc71`](https://github.com/nodejs/node/commit/ffeebebc71)] - **test**: make test-worker-prof more tolerant (Joyee Cheung) [#60272](https://github.com/nodejs/node/pull/60272) +* \[[`26b01bf170`](https://github.com/nodejs/node/commit/26b01bf170)] - **test**: skip sea tests on x64 macOS (Joyee Cheung) [#60250](https://github.com/nodejs/node/pull/60250) +* \[[`8caae1a05b`](https://github.com/nodejs/node/commit/8caae1a05b)] - **test**: move sea tests into test/sea (Joyee Cheung) [#60250](https://github.com/nodejs/node/pull/60250) +* \[[`3d183e3e9f`](https://github.com/nodejs/node/commit/3d183e3e9f)] - **test,crypto**: fix conditional SHA3-\* skip on BoringSSL (Filip Skokan) [#60379](https://github.com/nodejs/node/pull/60379) +* \[[`e83dbcba94`](https://github.com/nodejs/node/commit/e83dbcba94)] - **test,crypto**: sha3 algorithms aren't supported with BoringSSL (Shelley Vohr) [#60374](https://github.com/nodejs/node/pull/60374) +* \[[`3d89331496`](https://github.com/nodejs/node/commit/3d89331496)] - **test\_runner**: use module.registerHooks in module mocks (Joyee Cheung) [#60326](https://github.com/nodejs/node/pull/60326) +* \[[`377e8ce85a`](https://github.com/nodejs/node/commit/377e8ce85a)] - **tls**: avoid external memory leak on invalid protocol versions (Shelley Vohr) [#60390](https://github.com/nodejs/node/pull/60390) +* \[[`ae4858c1f6`](https://github.com/nodejs/node/commit/ae4858c1f6)] - **tools**: add an option to generate lighter archives (Antoine du Hamel) [#60294](https://github.com/nodejs/node/pull/60294) +* \[[`cb615b1a2e`](https://github.com/nodejs/node/commit/cb615b1a2e)] - **tools**: skip test-shared workflow for draft PRs (Michaël Zasso) [#60365](https://github.com/nodejs/node/pull/60365) +* \[[`03b034731e`](https://github.com/nodejs/node/commit/03b034731e)] - **tools**: disable inspector on macOS-shared to reduce flakiness (Antoine du Hamel) [#60320](https://github.com/nodejs/node/pull/60320) +* \[[`f402b4e1d1`](https://github.com/nodejs/node/commit/f402b4e1d1)] - **tools**: show diff alongside the error in Nix linter (Antoine du Hamel) [#60301](https://github.com/nodejs/node/pull/60301) +* \[[`5d5c8483fb`](https://github.com/nodejs/node/commit/5d5c8483fb)] - **tools**: run CI with shared libs on GHA (Antoine du Hamel) [#60121](https://github.com/nodejs/node/pull/60121) +* \[[`e8fdd8d2e8`](https://github.com/nodejs/node/commit/e8fdd8d2e8)] - **tools**: update gyp-next to 0.20.5 (Node.js GitHub Bot) [#60313](https://github.com/nodejs/node/pull/60313) +* \[[`6e8b029a21`](https://github.com/nodejs/node/commit/6e8b029a21)] - **tools**: limit inspector protocol PR title length (Chengzhong Wu) [#60324](https://github.com/nodejs/node/pull/60324) +* \[[`a5073086c6`](https://github.com/nodejs/node/commit/a5073086c6)] - **tools**: fix inspector\_protocol updater (Chengzhong Wu) [#60277](https://github.com/nodejs/node/pull/60277) +* \[[`47fa765bff`](https://github.com/nodejs/node/commit/47fa765bff)] - **tools**: optimize wildcard execution in tools/test.py (Joyee Cheung) [#60266](https://github.com/nodejs/node/pull/60266) +* \[[`11ebb0447d`](https://github.com/nodejs/node/commit/11ebb0447d)] - **tools**: add C++ lint rule to avoid using `String::Utf8Value` (Anna Henningsen) [#60244](https://github.com/nodejs/node/pull/60244) +* \[[`14f3189670`](https://github.com/nodejs/node/commit/14f3189670)] - **tools**: add inspector\_protocol updater (Chengzhong Wu) [#60245](https://github.com/nodejs/node/pull/60245) +* \[[`ef4c596fc6`](https://github.com/nodejs/node/commit/ef4c596fc6)] - **typings**: add missing properties and method in Worker (Woohyun Sung) [#60257](https://github.com/nodejs/node/pull/60257) +* \[[`09ae6fc065`](https://github.com/nodejs/node/commit/09ae6fc065)] - **typings**: add missing properties in HTTPParser (Woohyun Sung) [#60257](https://github.com/nodejs/node/pull/60257) +* \[[`9ecaf41f8e`](https://github.com/nodejs/node/commit/9ecaf41f8e)] - **typings**: delete undefined property in ConfigBinding (Woohyun Sung) [#60257](https://github.com/nodejs/node/pull/60257) +* \[[`4a86016e86`](https://github.com/nodejs/node/commit/4a86016e86)] - **util**: use more defensive code when inspecting error objects (Antoine du Hamel) [#60139](https://github.com/nodejs/node/pull/60139) +* \[[`9e6d6cec59`](https://github.com/nodejs/node/commit/9e6d6cec59)] - **util**: mark special properties when inspecting them (Ruben Bridgewater) [#60131](https://github.com/nodejs/node/pull/60131) +* \[[`79b2387fd9`](https://github.com/nodejs/node/commit/79b2387fd9)] - **vm**: make vm.Module.evaluate() conditionally synchronous (Joyee Cheung) [#60205](https://github.com/nodejs/node/pull/60205) +* \[[`e5559f3be3`](https://github.com/nodejs/node/commit/e5559f3be3)] - **win**: upgrade Visual Studio workload from 2019 to 2022 (Jiawen Geng) [#60318](https://github.com/nodejs/node/pull/60318) + ## 2025-10-15, Version 25.0.0 (Current), @RafaelGSS diff --git a/doc/contributing/writing-and-running-benchmarks.md b/doc/contributing/writing-and-running-benchmarks.md index a60a06b695f242..1dcb2a62a952bb 100644 --- a/doc/contributing/writing-and-running-benchmarks.md +++ b/doc/contributing/writing-and-running-benchmarks.md @@ -26,6 +26,10 @@ Basic Unix tools are required for some benchmarks. [Git for Windows][git-for-windows] includes Git Bash and the necessary tools, which need to be included in the global Windows `PATH`. +If you are using Nix, all the required tools are already listed in the +`benchmarkTools` argument of the `shell.nix` file, so you can skip those +prerequesites. + ### HTTP benchmark requirements Most of the HTTP benchmarks require a benchmarker to be installed. This can be diff --git a/doc/contributing/writing-tests.md b/doc/contributing/writing-tests.md index 7830bb3847184a..eb4e91b491eb9a 100644 --- a/doc/contributing/writing-tests.md +++ b/doc/contributing/writing-tests.md @@ -485,6 +485,11 @@ To generate a test coverage report, see the Nightly coverage reports for the Node.js `main` branch are available at . +## Running tests + +See the [Building guide](../../BUILDING.md#running-tests) for details on how to +run tests. + [ASCII]: https://man7.org/linux/man-pages/man7/ascii.7.html [Google Test]: https://github.com/google/googletest [Test Coverage section of the Building guide]: https://github.com/nodejs/node/blob/HEAD/BUILDING.md#running-coverage diff --git a/doc/node-config-schema.json b/doc/node-config-schema.json index 99c031199fbc1e..3f7d6a93de270d 100644 --- a/doc/node-config-schema.json +++ b/doc/node-config-schema.json @@ -734,6 +734,35 @@ "type": "boolean" } } + }, + "watch": { + "type": "object", + "additionalProperties": false, + "properties": { + "watch": { + "type": "boolean" + }, + "watch-kill-signal": { + "type": "string" + }, + "watch-path": { + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string", + "minItems": 1 + }, + "type": "array" + } + ] + }, + "watch-preserve-output": { + "type": "boolean" + } + } } }, "type": "object" diff --git a/lib/_http_incoming.js b/lib/_http_incoming.js index c3e901e53e8b90..6cda3a84cee065 100644 --- a/lib/_http_incoming.js +++ b/lib/_http_incoming.js @@ -423,6 +423,15 @@ function _addHeaderLineDistinct(field, value, dest) { } } +IncomingMessage.prototype._dumpAndCloseReadable = function _dumpAndCloseReadable() { + this._dumped = true; + this._readableState.ended = true; + this._readableState.endEmitted = true; + this._readableState.destroyed = true; + this._readableState.closed = true; + this._readableState.closeEmitted = true; +}; + // Call this instead of resume() if we want to just // dump all the data to /dev/null diff --git a/lib/_http_outgoing.js b/lib/_http_outgoing.js index fdd0f2f77eaac3..24aae1caca69d3 100644 --- a/lib/_http_outgoing.js +++ b/lib/_http_outgoing.js @@ -673,20 +673,22 @@ OutgoingMessage.prototype.setHeaders = function setHeaders(headers) { // We also cannot safely split by comma. // To avoid setHeader overwriting the previous value we push // set-cookie values in array and set them all at once. - const cookies = []; + let cookies = null; for (const { 0: key, 1: value } of headers) { if (key === 'set-cookie') { if (ArrayIsArray(value)) { + cookies ??= []; cookies.push(...value); } else { + cookies ??= []; cookies.push(value); } continue; } this.setHeader(key, value); } - if (cookies.length) { + if (cookies != null) { this.setHeader('set-cookie', cookies); } diff --git a/lib/_http_server.js b/lib/_http_server.js index f2817742aead4e..7a0f6447e20751 100644 --- a/lib/_http_server.js +++ b/lib/_http_server.js @@ -107,6 +107,8 @@ const onResponseFinishChannel = dc.channel('http.server.response.finish'); const kServerResponse = Symbol('ServerResponse'); const kServerResponseStatistics = Symbol('ServerResponseStatistics'); +const kOptimizeEmptyRequests = Symbol('OptimizeEmptyRequestsOption'); + const { hasObserver, startPerf, @@ -455,6 +457,11 @@ function storeHTTPOptions(options) { validateInteger(maxHeaderSize, 'maxHeaderSize', 0); this.maxHeaderSize = maxHeaderSize; + const optimizeEmptyRequests = options.optimizeEmptyRequests; + if (optimizeEmptyRequests !== undefined) + validateBoolean(optimizeEmptyRequests, 'options.optimizeEmptyRequests'); + this[kOptimizeEmptyRequests] = optimizeEmptyRequests || false; + const insecureHTTPParser = options.insecureHTTPParser; if (insecureHTTPParser !== undefined) validateBoolean(insecureHTTPParser, 'options.insecureHTTPParser'); @@ -1069,6 +1076,10 @@ function emitCloseNT(self) { } } +function hasBodyHeaders(headers) { + return ('content-length' in headers) || ('transfer-encoding' in headers); +} + // The following callback is issued after the headers have been read on a // new message. In this callback we setup the response object and pass it // to the user. @@ -1120,6 +1131,19 @@ function parserOnIncoming(server, socket, state, req, keepAlive) { }); } + // Check if we should optimize empty requests (those without Content-Length or Transfer-Encoding headers) + const shouldOptimize = server[kOptimizeEmptyRequests] === true && !hasBodyHeaders(req.headers); + + if (shouldOptimize) { + // Fast processing where emitting 'data', 'end' and 'close' events is + // skipped and data is dumped. + // This avoids a lot of unnecessary overhead otherwise introduced by + // stream.Readable life cycle rules. The downside is that this will + // break some servers that read bodies for methods that don't have body headers. + req._dumpAndCloseReadable(); + req._read(); + } + if (socket._httpMessage) { // There are already pending outgoing res, append. state.outgoing.push(res); diff --git a/lib/internal/blob.js b/lib/internal/blob.js index 7825ea15de1e6d..46abb4f793c465 100644 --- a/lib/internal/blob.js +++ b/lib/internal/blob.js @@ -478,7 +478,7 @@ function createBlobReaderStream(reader) { // We keep reading until we either reach EOS, some error, or we // hit the flow rate of the stream (c.desiredSize). // We use set immediate here because we have to allow the event - // loop to turn in order to proecss any pending i/o. Using + // loop to turn in order to process any pending i/o. Using // queueMicrotask won't allow the event loop to turn. setImmediate(() => { if (c.desiredSize < 0) { diff --git a/lib/internal/inspector/network_undici.js b/lib/internal/inspector/network_undici.js index b3f4f750d0ef19..a80af196587de4 100644 --- a/lib/internal/inspector/network_undici.js +++ b/lib/internal/inspector/network_undici.js @@ -209,23 +209,17 @@ function onClientResponseFinish({ request }) { // TODO: Move Network.webSocketCreated to the actual creation time of the WebSocket. // undici:websocket:open fires when the connection is established, but this results // in an inaccurate stack trace. -function onWebSocketOpen({ websocket }) { +function onWebSocketOpen({ websocket, handshakeResponse }) { websocket[kInspectorRequestId] = getNextRequestId(); const url = websocket.url.toString(); Network.webSocketCreated({ requestId: websocket[kInspectorRequestId], url, }); - // TODO: Use handshake response data from undici diagnostics when available. - // https://github.com/nodejs/undici/pull/4396 Network.webSocketHandshakeResponseReceived({ requestId: websocket[kInspectorRequestId], timestamp: getMonotonicTime(), - response: { - status: 101, - statusText: 'Switching Protocols', - headers: {}, - }, + response: handshakeResponse, }); } diff --git a/lib/internal/main/mksnapshot.js b/lib/internal/main/mksnapshot.js index 63df8c50087aad..b06b1a70bf4939 100644 --- a/lib/internal/main/mksnapshot.js +++ b/lib/internal/main/mksnapshot.js @@ -21,7 +21,6 @@ const { emitExperimentalWarning } = require('internal/util'); const { emitWarningSync } = require('internal/process/warning'); const { - initializeCallbacks, namespace: { addDeserializeCallback, isBuildingSnapshot, @@ -139,7 +138,6 @@ function requireForUserSnapshot(id) { function main() { prepareMainThreadExecution(false, false); - initializeCallbacks(); // In a context created for building snapshots, V8 does not install Error.stackTraceLimit and as // a result, if an error is created during the snapshot building process, error.stack would be diff --git a/lib/internal/main/watch_mode.js b/lib/internal/main/watch_mode.js index 37d126f1257b3e..bf70f7606f5e03 100644 --- a/lib/internal/main/watch_mode.js +++ b/lib/internal/main/watch_mode.js @@ -1,6 +1,7 @@ 'use strict'; const { ArrayPrototypeForEach, + ArrayPrototypeIncludes, ArrayPrototypeJoin, ArrayPrototypeMap, ArrayPrototypePush, @@ -17,7 +18,7 @@ const { triggerUncaughtException, exitCodes: { kNoFailure }, } = internalBinding('errors'); -const { getOptionValue } = require('internal/options'); +const { getOptionValue, getOptionsAsFlagsFromBinding } = require('internal/options'); const { FilesWatcher } = require('internal/watch_mode/files_watcher'); const { green, blue, red, white, clear } = require('internal/util/colors'); const { convertToValidSignal } = require('internal/util'); @@ -40,14 +41,14 @@ const kCommand = ArrayPrototypeSlice(process.argv, 1); const kCommandStr = inspect(ArrayPrototypeJoin(kCommand, ' ')); const argsWithoutWatchOptions = []; - -for (let i = 0; i < process.execArgv.length; i++) { - const arg = process.execArgv[i]; +const argsFromBinding = getOptionsAsFlagsFromBinding(); +for (let i = 0; i < argsFromBinding.length; i++) { + const arg = argsFromBinding[i]; if (StringPrototypeStartsWith(arg, '--watch=')) { continue; } if (arg === '--watch') { - const nextArg = process.execArgv[i + 1]; + const nextArg = argsFromBinding[i + 1]; if (nextArg && nextArg[0] !== '-') { // If `--watch` doesn't include `=` and the next // argument is not a flag then it is interpreted as @@ -66,6 +67,16 @@ for (let i = 0; i < process.execArgv.length; i++) { } continue; } + if (StringPrototypeStartsWith(arg, '--experimental-config-file')) { + if (!ArrayPrototypeIncludes(arg, '=')) { + // Skip the flag and the next argument (the config file path) + i++; + } + continue; + } + if (arg === '--experimental-default-config-file') { + continue; + } ArrayPrototypePush(argsWithoutWatchOptions, arg); } diff --git a/lib/internal/main/worker_thread.js b/lib/internal/main/worker_thread.js index 62dc44cf34b739..1e28a3452220e9 100644 --- a/lib/internal/main/worker_thread.js +++ b/lib/internal/main/worker_thread.js @@ -11,7 +11,6 @@ const { ObjectDefineProperty, PromisePrototypeThen, RegExpPrototypeExec, - SafeWeakMap, globalThis: { SharedArrayBuffer, }, @@ -19,7 +18,7 @@ const { const { prepareWorkerThreadExecution, - setupUserModules, + initializeModuleLoaders, markBootstrapComplete, } = require('internal/process/pre_execution'); @@ -138,11 +137,13 @@ port.on('message', (message) => { workerIo.sharedCwdCounter = cwdCounter; } - const isLoaderWorker = - doEval === 'internal' && - filename === require('internal/modules/esm/utils').loaderWorkerId; - // Disable custom loaders in loader worker. - setupUserModules(isLoaderWorker); + const isLoaderHookWorker = (filename === 'internal/modules/esm/worker' && doEval === 'internal'); + if (!isLoaderHookWorker) { + // If we are in the loader hook worker, delay the module loader initializations until + // initializeAsyncLoaderHooksOnLoaderHookWorker() which needs to run preloads + // after the asynchronous loader hooks are registered. + initializeModuleLoaders({ shouldSpawnLoaderHookWorker: true, shouldPreloadModules: true }); + } if (!hasStdin) process.stdin.push(null); @@ -152,9 +153,10 @@ port.on('message', (message) => { port.postMessage({ type: UP_AND_RUNNING }); switch (doEval) { case 'internal': { - // Create this WeakMap in js-land because V8 has no C++ API for WeakMap. - internalBinding('module_wrap').callbackMap = new SafeWeakMap(); - require(filename)(workerData, publicPort); + // Currently the only user of internal eval is the async loader hook thread. + assert(isLoaderHookWorker, `Unexpected internal eval ${filename}`); + const setupModuleWorker = require('internal/modules/esm/worker'); + setupModuleWorker(workerData, publicPort); break; } diff --git a/lib/internal/modules/cjs/loader.js b/lib/internal/modules/cjs/loader.js index 57470fb90dd907..8bd89305e2b20a 100644 --- a/lib/internal/modules/cjs/loader.js +++ b/lib/internal/modules/cjs/loader.js @@ -177,6 +177,7 @@ const { registerHooks, resolveHooks, resolveWithHooks, + validateLoadStrict, } = require('internal/modules/customization_hooks'); const { stripTypeScriptModuleTypes } = require('internal/modules/typescript'); const packageJsonReader = require('internal/modules/package_json_reader'); @@ -1175,7 +1176,7 @@ function loadBuiltinWithHooks(id, url, format) { url ??= `node:${id}`; // TODO(joyeecheung): do we really want to invoke the load hook for the builtins? const loadResult = loadWithHooks(url, format || 'builtin', /* importAttributes */ undefined, - getCjsConditionsArray(), getDefaultLoad(url, id)); + getCjsConditionsArray(), getDefaultLoad(url, id), validateLoadStrict); if (loadResult.format && loadResult.format !== 'builtin') { return undefined; // Format has been overridden, return undefined for the caller to continue loading. } @@ -1791,10 +1792,9 @@ function loadSource(mod, filename, formatFromNode) { mod[kURL] = convertCJSFilenameToURL(filename); } + const defaultLoad = getDefaultLoad(mod[kURL], filename); const loadResult = loadWithHooks(mod[kURL], mod[kFormat], /* importAttributes */ undefined, - getCjsConditionsArray(), - getDefaultLoad(mod[kURL], filename)); - + getCjsConditionsArray(), defaultLoad, validateLoadStrict); // Reset the module properties with load hook results. if (loadResult.format !== undefined) { mod[kFormat] = loadResult.format; diff --git a/lib/internal/modules/customization_hooks.js b/lib/internal/modules/customization_hooks.js index 580fc05467cd4f..c2579269ec6396 100644 --- a/lib/internal/modules/customization_hooks.js +++ b/lib/internal/modules/customization_hooks.js @@ -262,13 +262,25 @@ function validateResolve(specifier, context, result) { */ /** - * Validate the result returned by a chain of resolve hook. + * Validate the result returned by a chain of load hook. * @param {string} url URL passed into the hooks. * @param {ModuleLoadContext} context Context passed into the hooks. * @param {ModuleLoadResult} result Result produced by load hooks. * @returns {ModuleLoadResult} */ -function validateLoad(url, context, result) { +function validateLoadStrict(url, context, result) { + validateSourceStrict(url, context, result); + validateFormat(url, context, result); + return result; +} + +function validateLoadSloppy(url, context, result) { + validateSourcePermissive(url, context, result); + validateFormat(url, context, result); + return result; +} + +function validateSourceStrict(url, context, result) { const { source, format } = result; // To align with module.register(), the load hooks are still invoked for // the builtins even though the default load step only provides null as source, @@ -276,7 +288,8 @@ function validateLoad(url, context, result) { if (!StringPrototypeStartsWith(url, 'node:') && typeof result.source !== 'string' && !isAnyArrayBuffer(source) && - !isArrayBufferView(source)) { + !isArrayBufferView(source) && + format !== 'addon') { throw new ERR_INVALID_RETURN_PROPERTY_VALUE( 'a string, an ArrayBuffer, or a TypedArray', 'load', @@ -284,7 +297,21 @@ function validateLoad(url, context, result) { source, ); } +} +function validateSourcePermissive(url, context, result) { + const { source, format } = result; + if (format === 'commonjs' && source == null) { + // Accommodate the quirk in defaultLoad used by asynchronous loader hooks + // which sets source to null for commonjs. + // See: https://github.com/nodejs/node/issues/57327#issuecomment-2701382020 + return; + } + validateSourceStrict(url, context, result); +} + +function validateFormat(url, context, result) { + const { format } = result; if (typeof format !== 'string' && format !== undefined) { throw new ERR_INVALID_RETURN_PROPERTY_VALUE( 'a string', @@ -293,12 +320,6 @@ function validateLoad(url, context, result) { format, ); } - - return { - __proto__: null, - format, - source, - }; } class ModuleResolveContext { @@ -338,9 +359,10 @@ let decoder; * @param {ImportAttributes|undefined} importAttributes * @param {string[]} conditions * @param {(url: string, context: ModuleLoadContext) => ModuleLoadResult} defaultLoad + * @param {(url: string, context: ModuleLoadContext, result: ModuleLoadResult) => ModuleLoadResult} validateLoad * @returns {ModuleLoadResult} */ -function loadWithHooks(url, originalFormat, importAttributes, conditions, defaultLoad) { +function loadWithHooks(url, originalFormat, importAttributes, conditions, defaultLoad, validateLoad) { debug('loadWithHooks', url, originalFormat); const context = new ModuleLoadContext(originalFormat, importAttributes, conditions); if (loadHooks.length === 0) { @@ -403,4 +425,6 @@ module.exports = { registerHooks, resolveHooks, resolveWithHooks, + validateLoadStrict, + validateLoadSloppy, }; diff --git a/lib/internal/modules/esm/hooks.js b/lib/internal/modules/esm/hooks.js index 6e16d75d586944..cc66d47a43b704 100644 --- a/lib/internal/modules/esm/hooks.js +++ b/lib/internal/modules/esm/hooks.js @@ -54,14 +54,13 @@ const { } = require('internal/modules/esm/resolve'); const { getDefaultConditions, - loaderWorkerId, } = require('internal/modules/esm/utils'); const { deserializeError } = require('internal/error_serdes'); const { SHARED_MEMORY_BYTE_LENGTH, WORKER_TO_MAIN_THREAD_NOTIFICATION, } = require('internal/modules/esm/shared_constants'); -let debug = require('internal/util/debuglog').debuglog('esm', (fn) => { +let debug = require('internal/util/debuglog').debuglog('async_loader_worker', (fn) => { debug = fn; }); let importMetaInitializer; @@ -105,7 +104,39 @@ function defineImportAssertionAlias(context) { // [2] `validate...()`s throw the wrong error -class Hooks { +/** + * @typedef {{ format: ModuleFormat, source: ModuleSource }} LoadResult + */ + +/** + * @typedef {{ format: ModuleFormat, url: string, importAttributes: Record }} ResolveResult + */ + +/** + * Interface for classes that implement asynchronous loader hooks that can be attached to the ModuleLoader + * via `ModuleLoader.#setAsyncLoaderHooks()`. + * @typedef {object} AsyncLoaderHooks + * @property {boolean} allowImportMetaResolve Whether to allow the use of `import.meta.resolve`. + * @property {(url: string, context: object, defaultLoad: Function) => Promise} load + * Calling the asynchronous `load` hook asynchronously. + * @property {(url: string, context: object, defaultLoad: Function) => LoadResult} loadSync + * Calling the asynchronous `load` hook synchronously. + * @property {(originalSpecifier: string, parentURL: string, + * importAttributes: Record) => Promise} resolve + * Calling the asynchronous `resolve` hook asynchronously. + * @property {(originalSpecifier: string, parentURL: string, + * importAttributes: Record) => ResolveResult} resolveSync + * Calling the asynchronous `resolve` hook synchronously. + * @property {(specifier: string, parentURL: string) => any} register Register asynchronous loader hooks + * @property {() => void} waitForLoaderHookInitialization Force loading of hooks. + */ + +/** + * @implements {AsyncLoaderHooks} + * Instances of this class run directly on the loader hook worker thread and customize the module + * loading of the hooks worker itself. + */ +class AsyncLoaderHooksOnLoaderHookWorker { #chains = { /** * Phase 1 of 2 in ESM loading. @@ -452,7 +483,7 @@ class Hooks { }; } - forceLoadHooks() { + waitForLoaderHookInitialization() { // No-op } @@ -462,14 +493,20 @@ class Hooks { return meta; } } -ObjectSetPrototypeOf(Hooks.prototype, null); +ObjectSetPrototypeOf(AsyncLoaderHooksOnLoaderHookWorker.prototype, null); /** - * There may be multiple instances of Hooks/HooksProxy, but there is only 1 Internal worker, so - * there is only 1 MessageChannel. + * There is only one loader hook thread for each non-loader-hook worker thread + * (i.e. the non-loader-hook thread and any worker threads that are not loader hook workers themselves), + * so there is only 1 MessageChannel. */ let MessageChannel; -class HooksProxy { + +/** + * Abstraction over a worker thread that runs the asynchronous module loader hooks. + * Instances of this class run on the non-loader-hook thread and communicate with the loader hooks worker thread. + */ +class AsyncLoaderHookWorker { /** * Shared memory. Always use Atomics method to read or write to it. * @type {Int32Array} @@ -503,7 +540,7 @@ class HooksProxy { const lock = new SharedArrayBuffer(SHARED_MEMORY_BYTE_LENGTH); this.#lock = new Int32Array(lock); - this.#worker = new InternalWorker(loaderWorkerId, { + this.#worker = new InternalWorker('internal/modules/esm/worker', { stderr: false, stdin: false, stdout: false, @@ -644,7 +681,7 @@ class HooksProxy { this.#importMetaInitializer(meta, context, loader); } } -ObjectSetPrototypeOf(HooksProxy.prototype, null); +ObjectSetPrototypeOf(AsyncLoaderHookWorker.prototype, null); // TODO(JakobJingleheimer): Remove this when loaders go "stable". let globalPreloadWarningWasEmitted = false; @@ -757,6 +794,95 @@ function nextHookFactory(current, meta, { validateArgs, validateOutput }) { ); } +/** + * @type {AsyncLoaderHookWorker} + * Worker instance used to run async loader hooks in a separate thread. This is a singleton for each + * non-loader-hook worker thread (i.e. the main thread and any worker threads that are not + * loader hook workers themselves). + */ +let asyncLoaderHookWorker; +/** + * Get the AsyncLoaderHookWorker instance. If it is not defined, then create a new one. + * @returns {AsyncLoaderHookWorker} + */ +function getAsyncLoaderHookWorker() { + asyncLoaderHookWorker ??= new AsyncLoaderHookWorker(); + return asyncLoaderHookWorker; +} + +/** + * @implements {AsyncLoaderHooks} + * Instances of this class are created in the non-loader-hook thread and communicate with the worker thread + * spawned to run the async loader hooks. + */ +class AsyncLoaderHooksProxiedToLoaderHookWorker { + + allowImportMetaResolve = true; + + /** + * Instantiate a module loader that uses user-provided custom loader hooks. + */ + constructor() { + getAsyncLoaderHookWorker(); + } + + /** + * Register some loader specifier. + * @param {string} originalSpecifier The specified URL path of the loader to + * be registered. + * @param {string} parentURL The parent URL from where the loader will be + * registered if using it package name as specifier + * @param {any} [data] Arbitrary data to be passed from the custom loader + * (user-land) to the worker. + * @param {any[]} [transferList] Objects in `data` that are changing ownership + * @param {boolean} [isInternal] For internal loaders that should not be publicly exposed. + * @returns {{ format: string, url: URL['href'] }} + */ + register(originalSpecifier, parentURL, data, transferList, isInternal) { + return asyncLoaderHookWorker.makeSyncRequest('register', transferList, originalSpecifier, parentURL, + data, isInternal); + } + + /** + * Resolve the location of the module. + * @param {string} originalSpecifier The specified URL path of the module to + * be resolved. + * @param {string} [parentURL] The URL path of the module's parent. + * @param {ImportAttributes} importAttributes Attributes from the import + * statement or expression. + * @returns {{ format: string, url: URL['href'] }} + */ + resolve(originalSpecifier, parentURL, importAttributes) { + return asyncLoaderHookWorker.makeAsyncRequest('resolve', undefined, originalSpecifier, parentURL, importAttributes); + } + + resolveSync(originalSpecifier, parentURL, importAttributes) { + // This happens only as a result of `import.meta.resolve` calls, which must be sync per spec. + return asyncLoaderHookWorker.makeSyncRequest('resolve', undefined, originalSpecifier, parentURL, importAttributes); + } + + /** + * Provide source that is understood by one of Node's translators. + * @param {URL['href']} url The URL/path of the module to be loaded + * @param {object} [context] Metadata about the module + * @returns {Promise<{ format: ModuleFormat, source: ModuleSource }>} + */ + load(url, context) { + return asyncLoaderHookWorker.makeAsyncRequest('load', undefined, url, context); + } + loadSync(url, context) { + return asyncLoaderHookWorker.makeSyncRequest('load', undefined, url, context); + } + + importMetaInitialize(meta, context, loader) { + asyncLoaderHookWorker.importMetaInitialize(meta, context, loader); + } + + waitForLoaderHookInitialization() { + asyncLoaderHookWorker.waitForWorker(); + } +} -exports.Hooks = Hooks; -exports.HooksProxy = HooksProxy; +exports.AsyncLoaderHooksProxiedToLoaderHookWorker = AsyncLoaderHooksProxiedToLoaderHookWorker; +exports.AsyncLoaderHooksOnLoaderHookWorker = AsyncLoaderHooksOnLoaderHookWorker; +exports.AsyncLoaderHookWorker = AsyncLoaderHookWorker; diff --git a/lib/internal/modules/esm/load.js b/lib/internal/modules/esm/load.js index 8414d303c078d5..c284163fba86ec 100644 --- a/lib/internal/modules/esm/load.js +++ b/lib/internal/modules/esm/load.js @@ -141,15 +141,26 @@ function defaultLoadSync(url, context = kEmptyObject) { throwIfUnsupportedURLScheme(urlInstance, false); + let shouldBeReloadedByCJSLoader = false; if (urlInstance.protocol === 'node:') { source = null; - } else if (source == null) { - ({ responseURL, source } = getSourceSync(urlInstance, context)); - context.source = source; - } + format ??= 'builtin'; + } else if (format === 'addon') { + // Skip loading addon file content. It must be loaded with dlopen from file system. + source = null; + } else { + if (source == null) { + ({ responseURL, source } = getSourceSync(urlInstance, context)); + context = { __proto__: context, source }; + } - format ??= defaultGetFormat(urlInstance, context); + // Now that we have the source for the module, run `defaultGetFormat` to detect its format. + format ??= defaultGetFormat(urlInstance, context); + // For backward compatibility reasons, we need to let go through Module._load + // again. + shouldBeReloadedByCJSLoader = (format === 'commonjs'); + } validateAttributes(url, format, importAttributes); return { @@ -157,6 +168,7 @@ function defaultLoadSync(url, context = kEmptyObject) { format, responseURL, source, + shouldBeReloadedByCJSLoader, }; } diff --git a/lib/internal/modules/esm/loader.js b/lib/internal/modules/esm/loader.js index 300da51afe6185..d536d8215c93ab 100644 --- a/lib/internal/modules/esm/loader.js +++ b/lib/internal/modules/esm/loader.js @@ -34,7 +34,7 @@ const { kEmptyObject } = require('internal/util'); const { compileSourceTextModule, getDefaultConditions, - forceDefaultLoader, + shouldSpawnLoaderHookWorker, } = require('internal/modules/esm/utils'); const { kImplicitTypeAttribute } = require('internal/modules/esm/assert'); const { @@ -51,12 +51,13 @@ const { urlToFilename, } = require('internal/modules/helpers'); const { - resolveHooks, - resolveWithHooks, - loadHooks, - loadWithHooks, + resolveHooks: syncResolveHooks, + resolveWithHooks: resolveWithSyncHooks, + loadHooks: syncLoadHooks, + loadWithHooks: loadWithSyncHooks, + validateLoadSloppy, } = require('internal/modules/customization_hooks'); -let defaultResolve, defaultLoad, defaultLoadSync, importMetaInitializer; +let defaultResolve, defaultLoadSync, importMetaInitializer; const { tracingChannel } = require('diagnostics_channel'); const onImport = tracingChannel('module.import'); @@ -68,7 +69,7 @@ let debug = require('internal/util/debuglog').debuglog('esm', (fn) => { const { isPromise } = require('internal/util/types'); /** - * @typedef {import('./hooks.js').HooksProxy} HooksProxy + * @typedef {import('./hooks.js').AsyncLoaderHookWorker} AsyncLoaderHookWorker * @typedef {import('./module_job.js').ModuleJobBase} ModuleJobBase * @typedef {import('url').URL} URL */ @@ -122,14 +123,6 @@ function getRaceMessage(filename, parentFilename) { return raceMessage; } -/** - * @type {HooksProxy} - * Multiple loader instances exist for various, specific reasons (see code comments at site). - * In order to maintain consistency, we use a single worker (sandbox), which must sit apart of an - * individual loader instance. - */ -let hooksProxy; - /** * @typedef {import('../cjs/loader.js').Module} CJSModule */ @@ -147,9 +140,21 @@ let hooksProxy; */ /** - * This class covers the base machinery of module loading. To add custom - * behavior you can pass a customizations object and this object will be - * used to do the loading/resolving/registration process. + * @typedef {{ format: ModuleFormat, source: ModuleSource, translatorKey: string }} TranslateContext + */ + +/** + * @typedef {import('./hooks.js').AsyncLoaderHooks} AsyncLoaderHooks + * @typedef {import('./hooks.js').AsyncLoaderHooksOnLoaderHookWorker} AsyncLoaderHooksOnLoaderHookWorker + * @typedef {import('./hooks.js').AsyncLoaderHooksProxiedToLoaderHookWorker} AsyncLoaderHooksProxiedToLoaderHookWorker + */ + +/** + * This class covers the base machinery of module loading. There are two types of loader hooks: + * 1. Asynchronous loader hooks, which are run in a separate loader hook worker thread. + * This is configured in #asyncLoaderHooks. + * 2. Synchronous loader hooks, which are run in-thread. This is shared with the CJS loader and is + * stored in the cross-module syncResolveHooks and syncLoadHooks arrays. */ class ModuleLoader { /** @@ -180,73 +185,44 @@ class ModuleLoader { allowImportMetaResolve; /** - * Customizations to pass requests to. - * @type {import('./hooks.js').Hooks} - * Note that this value _MUST_ be set with `setCustomizations` - * because it needs to copy `customizations.allowImportMetaResolve` + * Asynchronous loader hooks to pass requests to. + * + * Note that this value _MUST_ be set with `#setAsyncLoaderHooks` + * because it needs to copy `#asyncLoaderHooks.allowImportMetaResolve` * to this property and failure to do so will cause undefined * behavior when invoking `import.meta.resolve`. - * @see {ModuleLoader.setCustomizations} - * @type {CustomizedModuleLoader} + * + * When the ModuleLoader is created on a loader hook thread, this is + * {@link AsyncLoaderHooksOnLoaderHookWorker}, and its methods directly call out + * to loader methods. Otherwise, this is {@link AsyncLoaderHooksProxiedToLoaderHookWorker}, + * and its methods post messages to the loader thread and possibly block on it. + * @see {ModuleLoader.#setAsyncLoaderHooks} + * @type {AsyncLoaderHooks} */ - #customizations; + #asyncLoaderHooks; - constructor(customizations) { - this.setCustomizations(customizations); + constructor(asyncLoaderHooks) { + this.#setAsyncLoaderHooks(asyncLoaderHooks); } /** - * Change the currently activate customizations for this module - * loader to be the provided `customizations`. + * Change the currently activate async loader hooks for this module + * loader to be the provided `AsyncLoaderHooks`. * * If present, this class customizes its core functionality to the - * `customizations` object, including registration, loading, and resolving. + * `AsyncLoaderHooks` object, including registration, loading, and resolving. * There are some responsibilities that this class _always_ takes - * care of, like validating outputs, so that the customizations object + * care of, like validating outputs, so that the AsyncLoaderHooks object * does not have to do so. * - * The customizations object has the shape: - * - * ```ts - * interface LoadResult { - * format: ModuleFormat; - * source: ModuleSource; - * } - * - * interface ResolveResult { - * format: string; - * url: URL['href']; - * } - * - * interface Customizations { - * allowImportMetaResolve: boolean; - * load(url: string, context: object): Promise - * resolve( - * originalSpecifier: - * string, parentURL: string, - * importAttributes: Record - * ): Promise - * resolveSync( - * originalSpecifier: - * string, parentURL: string, - * importAttributes: Record - * ) ResolveResult; - * register(specifier: string, parentURL: string): any; - * forceLoadHooks(): void; - * } - * ``` - * - * Note that this class _also_ implements the `Customizations` - * interface, as does `CustomizedModuleLoader` and `Hooks`. - * * Calling this function alters how modules are loaded and should be * invoked with care. - * @param {CustomizedModuleLoader} customizations + * @param {AsyncLoaderHooks} asyncLoaderHooks */ - setCustomizations(customizations) { - this.#customizations = customizations; - if (customizations) { - this.allowImportMetaResolve = customizations.allowImportMetaResolve; + #setAsyncLoaderHooks(asyncLoaderHooks) { + this.#asyncLoaderHooks = asyncLoaderHooks; + if (asyncLoaderHooks) { + this.allowImportMetaResolve = asyncLoaderHooks.allowImportMetaResolve; } else { this.allowImportMetaResolve = true; } @@ -503,18 +479,19 @@ class ModuleLoader { const loadResult = this.#loadSync(url, { format, importAttributes }); + const formatFromLoad = loadResult.format; // Use the synchronous commonjs translator which can deal with cycles. - const finalFormat = - loadResult.format === 'commonjs' || - loadResult.format === 'commonjs-typescript' ? 'commonjs-sync' : loadResult.format; + const translatorKey = (formatFromLoad === 'commonjs' || formatFromLoad === 'commonjs-typescript') ? + 'commonjs-sync' : formatFromLoad; - if (finalFormat === 'wasm') { + if (translatorKey === 'wasm') { assert.fail('WASM is currently unsupported by require(esm)'); } const { source } = loadResult; const isMain = (parentURL === undefined); - const wrap = this.#translate(url, finalFormat, source, parentURL); + const translateContext = { format: formatFromLoad, source, translatorKey, __proto__: null }; + const wrap = this.#translate(url, translateContext, parentURL); assert(wrap instanceof ModuleWrap, `Translator used for require(${url}) should not be async`); if (process.env.WATCH_REPORT_DEPENDENCIES && process.send) { @@ -523,7 +500,7 @@ class ModuleLoader { const cjsModule = wrap[imported_cjs_symbol]; if (cjsModule) { - assert(finalFormat === 'commonjs-sync'); + assert(translatorKey === 'commonjs-sync'); // Check if the ESM initiating import CJS is being required by the same CJS module. if (cjsModule?.[kIsExecuting]) { const parentFilename = urlToFilename(parentURL); @@ -547,22 +524,22 @@ class ModuleLoader { * Translate a loaded module source into a ModuleWrap. This is run synchronously, * but the translator may return the ModuleWrap in a Promise. * @param {string} url URL of the module to be translated. - * @param {string} format Format of the module to be translated. This is used to find - * matching translators. - * @param {ModuleSource} source Source of the module to be translated. - * @param {string|undefined} parentURL URL of the parent module. Undefined if it's the entry point. + * @param {TranslateContext} translateContext Context for the translator + * @param {string|undefined} parentURL URL of the module initiating the module loading for the first time. + * Undefined if it's the entry point. * @returns {ModuleWrap} */ - #translate(url, format, source, parentURL) { + #translate(url, translateContext, parentURL) { + const { translatorKey, format } = translateContext; this.validateLoadResult(url, format); - const translator = getTranslators().get(format); + const translator = getTranslators().get(translatorKey); if (!translator) { - throw new ERR_UNKNOWN_MODULE_FORMAT(format, url); + throw new ERR_UNKNOWN_MODULE_FORMAT(translatorKey, url); } - const result = FunctionPrototypeCall(translator, this, url, source, parentURL === undefined); - assert(result instanceof ModuleWrap); + const result = FunctionPrototypeCall(translator, this, url, translateContext, parentURL); + assert(result instanceof ModuleWrap, `The ${format} module returned is not a ModuleWrap`); return result; } @@ -575,7 +552,8 @@ class ModuleLoader { * @returns {ModuleWrap} */ loadAndTranslateForRequireInImportedCJS(url, loadContext, parentURL) { - const { format: formatFromLoad, source } = this.#loadSync(url, loadContext); + const loadResult = this.#loadSync(url, loadContext); + const formatFromLoad = loadResult.format; if (formatFromLoad === 'wasm') { // require(wasm) is not supported. throw new ERR_UNKNOWN_MODULE_FORMAT(formatFromLoad, url); @@ -587,15 +565,16 @@ class ModuleLoader { } } - let finalFormat = formatFromLoad; + let translatorKey = formatFromLoad; if (formatFromLoad === 'commonjs') { - finalFormat = 'require-commonjs'; + translatorKey = 'require-commonjs'; } if (formatFromLoad === 'commonjs-typescript') { - finalFormat = 'require-commonjs-typescript'; + translatorKey = 'require-commonjs-typescript'; } - const wrap = this.#translate(url, finalFormat, source, parentURL); + const translateContext = { ...loadResult, translatorKey, __proto__: null }; + const wrap = this.#translate(url, translateContext, parentURL); assert(wrap instanceof ModuleWrap, `Translator used for require(${url}) should not be async`); return wrap; } @@ -610,8 +589,9 @@ class ModuleLoader { */ loadAndTranslate(url, loadContext, parentURL) { const maybePromise = this.load(url, loadContext); - const afterLoad = ({ format, source }) => { - return this.#translate(url, format, source, parentURL); + const afterLoad = (loadResult) => { + const translateContext = { ...loadResult, translatorKey: loadResult.format, __proto__: null }; + return this.#translate(url, translateContext, parentURL); }; if (isPromise(maybePromise)) { return maybePromise.then(afterLoad); @@ -698,18 +678,18 @@ class ModuleLoader { } /** - * @see {@link CustomizedModuleLoader.register} + * @see {@link AsyncLoaderHooks.register} * @returns {any} */ register(specifier, parentURL, data, transferList, isInternal) { - if (!this.#customizations) { - // `CustomizedModuleLoader` is defined at the bottom of this file and - // available well before this line is ever invoked. This is here in - // order to preserve the git diff instead of moving the class. - // eslint-disable-next-line no-use-before-define - this.setCustomizations(new CustomizedModuleLoader()); + if (!this.#asyncLoaderHooks) { + // On the loader hook worker thread, the #asyncLoaderHooks must already have been initialized + // to be an instance of AsyncLoaderHooksOnLoaderHookWorker, so this branch can only ever + // be hit on a non-loader-hook thread that will talk to the loader hook worker thread. + const { AsyncLoaderHooksProxiedToLoaderHookWorker } = require('internal/modules/esm/hooks'); + this.#setAsyncLoaderHooks(new AsyncLoaderHooksProxiedToLoaderHookWorker()); } - return this.#customizations.register(`${specifier}`, `${parentURL}`, data, transferList, isInternal); + return this.#asyncLoaderHooks.register(`${specifier}`, `${parentURL}`, data, transferList, isInternal); } /** @@ -724,12 +704,12 @@ class ModuleLoader { */ resolve(specifier, parentURL, importAttributes) { specifier = `${specifier}`; - if (resolveHooks.length) { + if (syncResolveHooks.length) { // Has module.registerHooks() hooks, use the synchronous variant that can handle both hooks. return this.resolveSync(specifier, parentURL, importAttributes); } - if (this.#customizations) { // Only has module.register hooks. - return this.#customizations.resolve(specifier, parentURL, importAttributes); + if (this.#asyncLoaderHooks) { // Only has module.register hooks. + return this.#asyncLoaderHooks.resolve(specifier, parentURL, importAttributes); } return this.#cachedDefaultResolve(specifier, { __proto__: null, @@ -787,8 +767,8 @@ class ModuleLoader { * @returns {{ format: string, url: string }} */ #resolveAndMaybeBlockOnLoaderThread(specifier, context) { - if (this.#customizations) { - return this.#customizations.resolveSync(specifier, context.parentURL, context.importAttributes); + if (this.#asyncLoaderHooks) { + return this.#asyncLoaderHooks.resolveSync(specifier, context.parentURL, context.importAttributes); } return this.#cachedDefaultResolve(specifier, context); } @@ -808,10 +788,10 @@ class ModuleLoader { */ resolveSync(specifier, parentURL, importAttributes = { __proto__: null }) { specifier = `${specifier}`; - if (resolveHooks.length) { + if (syncResolveHooks.length) { // Has module.registerHooks() hooks, chain the asynchronous hooks in the default step. - return resolveWithHooks(specifier, parentURL, importAttributes, this.#defaultConditions, - this.#resolveAndMaybeBlockOnLoaderThread.bind(this)); + return resolveWithSyncHooks(specifier, parentURL, importAttributes, this.#defaultConditions, + this.#resolveAndMaybeBlockOnLoaderThread.bind(this)); } return this.#resolveAndMaybeBlockOnLoaderThread(specifier, { __proto__: null, @@ -829,16 +809,16 @@ class ModuleLoader { * @returns {Promise<{ format: ModuleFormat, source: ModuleSource }> | { format: ModuleFormat, source: ModuleSource }} */ load(url, context) { - if (loadHooks.length) { + if (syncLoadHooks.length) { // Has module.registerHooks() hooks, use the synchronous variant that can handle both hooks. return this.#loadSync(url, context); } - if (this.#customizations) { - return this.#customizations.load(url, context); + if (this.#asyncLoaderHooks) { + return this.#asyncLoaderHooks.load(url, context); } - defaultLoad ??= require('internal/modules/esm/load').defaultLoad; - return defaultLoad(url, context); + defaultLoadSync ??= require('internal/modules/esm/load').defaultLoadSync; + return defaultLoadSync(url, context); } /** @@ -849,8 +829,8 @@ class ModuleLoader { * @returns {{ format: ModuleFormat, source: ModuleSource }} */ #loadAndMaybeBlockOnLoaderThread(url, context) { - if (this.#customizations) { - return this.#customizations.loadSync(url, context); + if (this.#asyncLoaderHooks) { + return this.#asyncLoaderHooks.loadSync(url, context); } defaultLoadSync ??= require('internal/modules/esm/load').defaultLoadSync; return defaultLoadSync(url, context); @@ -868,12 +848,12 @@ class ModuleLoader { * @returns {{ format: ModuleFormat, source: ModuleSource }} */ #loadSync(url, context) { - if (loadHooks.length) { + if (syncLoadHooks.length) { // Has module.registerHooks() hooks, chain the asynchronous hooks in the default step. // TODO(joyeecheung): construct the ModuleLoadContext in the loaders directly instead // of converting them from plain objects in the hooks. - return loadWithHooks(url, context.format, context.importAttributes, this.#defaultConditions, - this.#loadAndMaybeBlockOnLoaderThread.bind(this)); + return loadWithSyncHooks(url, context.format, context.importAttributes, this.#defaultConditions, + this.#loadAndMaybeBlockOnLoaderThread.bind(this), validateLoadSloppy); } return this.#loadAndMaybeBlockOnLoaderThread(url, context); } @@ -885,8 +865,8 @@ class ModuleLoader { } importMetaInitialize(meta, context) { - if (this.#customizations) { - return this.#customizations.importMetaInitialize(meta, context, this); + if (this.#asyncLoaderHooks) { + return this.#asyncLoaderHooks.importMetaInitialize(meta, context, this); } importMetaInitializer ??= require('internal/modules/esm/initialize_import_meta').initializeImportMeta; meta = importMetaInitializer(meta, context, this); @@ -894,94 +874,29 @@ class ModuleLoader { } /** + * Block until the async loader hooks have been initialized. + * * No-op when no hooks have been supplied. */ - forceLoadHooks() { - this.#customizations?.forceLoadHooks(); + waitForAsyncLoaderHookInitialization() { + this.#asyncLoaderHooks?.waitForLoaderHookInitialization(); } } ObjectSetPrototypeOf(ModuleLoader.prototype, null); -class CustomizedModuleLoader { - - allowImportMetaResolve = true; - - /** - * Instantiate a module loader that uses user-provided custom loader hooks. - */ - constructor() { - getHooksProxy(); - } - - /** - * Register some loader specifier. - * @param {string} originalSpecifier The specified URL path of the loader to - * be registered. - * @param {string} parentURL The parent URL from where the loader will be - * registered if using it package name as specifier - * @param {any} [data] Arbitrary data to be passed from the custom loader - * (user-land) to the worker. - * @param {any[]} [transferList] Objects in `data` that are changing ownership - * @param {boolean} [isInternal] For internal loaders that should not be publicly exposed. - * @returns {{ format: string, url: URL['href'] }} - */ - register(originalSpecifier, parentURL, data, transferList, isInternal) { - return hooksProxy.makeSyncRequest('register', transferList, originalSpecifier, parentURL, data, isInternal); - } - - /** - * Resolve the location of the module. - * @param {string} originalSpecifier The specified URL path of the module to - * be resolved. - * @param {string} [parentURL] The URL path of the module's parent. - * @param {ImportAttributes} importAttributes Attributes from the import - * statement or expression. - * @returns {{ format: string, url: URL['href'] }} - */ - resolve(originalSpecifier, parentURL, importAttributes) { - return hooksProxy.makeAsyncRequest('resolve', undefined, originalSpecifier, parentURL, importAttributes); - } - - resolveSync(originalSpecifier, parentURL, importAttributes) { - // This happens only as a result of `import.meta.resolve` calls, which must be sync per spec. - return hooksProxy.makeSyncRequest('resolve', undefined, originalSpecifier, parentURL, importAttributes); - } - - /** - * Provide source that is understood by one of Node's translators. - * @param {URL['href']} url The URL/path of the module to be loaded - * @param {object} [context] Metadata about the module - * @returns {Promise<{ format: ModuleFormat, source: ModuleSource }>} - */ - load(url, context) { - return hooksProxy.makeAsyncRequest('load', undefined, url, context); - } - loadSync(url, context) { - return hooksProxy.makeSyncRequest('load', undefined, url, context); - } - - importMetaInitialize(meta, context, loader) { - hooksProxy.importMetaInitialize(meta, context, loader); - } - - forceLoadHooks() { - hooksProxy.waitForWorker(); - } -} - let emittedLoaderFlagWarning = false; /** * A loader instance is used as the main entry point for loading ES modules. Currently, this is a singleton; there is * only one used for loading the main module and everything in its dependency graph, though separate instances of this * class might be instantiated as part of bootstrap for other purposes. + * @param {AsyncLoaderHooksOnLoaderHookWorker|undefined} [asyncLoaderHooks] + * Only provided when run on the loader hook thread. * @returns {ModuleLoader} */ -function createModuleLoader() { - let customizations = null; - // Don't spawn a new worker if custom loaders are disabled. For instance, if - // we're already in a worker thread created by instantiating - // CustomizedModuleLoader; doing so would cause an infinite loop. - if (!forceDefaultLoader()) { +function createModuleLoader(asyncLoaderHooks) { + // Don't spawn a new loader hook worker if we are already in a loader hook worker to avoid infinite recursion. + if (shouldSpawnLoaderHookWorker()) { + assert(asyncLoaderHooks === undefined, 'asyncLoaderHooks should only be provided on the loader hook thread itself'); const userLoaderPaths = getOptionValue('--experimental-loader'); if (userLoaderPaths.length > 0) { if (!emittedLoaderFlagWarning) { @@ -1003,44 +918,37 @@ function createModuleLoader() { ); emittedLoaderFlagWarning = true; } - customizations = new CustomizedModuleLoader(); + const { AsyncLoaderHooksProxiedToLoaderHookWorker } = require('internal/modules/esm/hooks'); + asyncLoaderHooks = new AsyncLoaderHooksProxiedToLoaderHookWorker(); } } - return new ModuleLoader(customizations); -} - - -/** - * Get the HooksProxy instance. If it is not defined, then create a new one. - * @returns {HooksProxy} - */ -function getHooksProxy() { - if (!hooksProxy) { - const { HooksProxy } = require('internal/modules/esm/hooks'); - hooksProxy = new HooksProxy(); - } - - return hooksProxy; + return new ModuleLoader(asyncLoaderHooks); } let cascadedLoader; /** * This is a singleton ESM loader that integrates the loader hooks, if any. - * It it used by other internal built-ins when they need to load ESM code + * It it used by other internal built-ins when they need to load user-land ESM code * while also respecting hooks. * When built-ins need access to this loader, they should do * require('internal/module/esm/loader').getOrInitializeCascadedLoader() * lazily only right before the loader is actually needed, and don't do it * in the top-level, to avoid circular dependencies. + * @param {AsyncLoaderHooksOnLoaderHookWorker|undefined} [asyncLoaderHooks] + * Only provided when run on the loader hook thread. * @returns {ModuleLoader} */ -function getOrInitializeCascadedLoader() { - cascadedLoader ??= createModuleLoader(); +function getOrInitializeCascadedLoader(asyncLoaderHooks) { + cascadedLoader ??= createModuleLoader(asyncLoaderHooks); return cascadedLoader; } +function isCascadedLoaderInitialized() { + return cascadedLoader !== undefined; +} + /** * Register a single loader programmatically. * @param {string|URL} specifier @@ -1085,7 +993,7 @@ function register(specifier, parentURL = undefined, options) { module.exports = { createModuleLoader, - getHooksProxy, getOrInitializeCascadedLoader, + isCascadedLoaderInitialized, register, }; diff --git a/lib/internal/modules/esm/translators.js b/lib/internal/modules/esm/translators.js index 147d96bda8098f..446349113e13bc 100644 --- a/lib/internal/modules/esm/translators.js +++ b/lib/internal/modules/esm/translators.js @@ -100,10 +100,12 @@ function errPath(url) { } // Strategy for loading a standard JavaScript module. -translators.set('module', function moduleStrategy(url, source, isMain) { +translators.set('module', function moduleStrategy(url, translateContext, parentURL) { + let { source } = translateContext; + const isMain = (parentURL === undefined); assertBufferSource(source, true, 'load'); source = stringify(source); - debug(`Translating StandardModule ${url}`); + debug(`Translating StandardModule ${url}`, translateContext); const { compileSourceTextModule } = require('internal/modules/esm/utils'); const context = isMain ? { isMain } : undefined; const module = compileSourceTextModule(url, source, this, context); @@ -199,20 +201,23 @@ const cjsCache = new SafeMap(); /** * Creates a ModuleWrap object for a CommonJS module. * @param {string} url - The URL of the module. - * @param {string} source - The source code of the module. - * @param {boolean} isMain - Whether the module is the main module. - * @param {string} format - Format of the module. + * @param {{ format: ModuleFormat, source: ModuleSource }} translateContext Context for the translator + * @param {string|undefined} parentURL URL of the module initiating the module loading for the first time. + * Undefined if it's the entry point. * @param {typeof loadCJSModule} [loadCJS] - The function to load the CommonJS module. * @returns {ModuleWrap} The ModuleWrap object for the CommonJS module. */ -function createCJSModuleWrap(url, source, isMain, format, loadCJS = loadCJSModule) { - debug(`Translating CJSModule ${url}`); +function createCJSModuleWrap(url, translateContext, parentURL, loadCJS = loadCJSModule) { + debug(`Translating CJSModule ${url}`, translateContext); + const { format: sourceFormat } = translateContext; + let { source } = translateContext; + const isMain = (parentURL === undefined); const filename = urlToFilename(url); // In case the source was not provided by the `load` step, we need fetch it now. source = stringify(source ?? getSource(new URL(url)).source); - const { exportNames, module } = cjsPreparseModuleExports(filename, source, format); + const { exportNames, module } = cjsPreparseModuleExports(filename, source, sourceFormat); cjsCache.set(url, module); const wrapperNames = [...exportNames]; @@ -263,11 +268,12 @@ function createCJSModuleWrap(url, source, isMain, format, loadCJS = loadCJSModul /** * Creates a ModuleWrap object for a CommonJS module without source texts. * @param {string} url - The URL of the module. - * @param {boolean} isMain - Whether the module is the main module. + * @param {string|undefined} parentURL - URL of the parent module, if any. * @returns {ModuleWrap} The ModuleWrap object for the CommonJS module. */ -function createCJSNoSourceModuleWrap(url, isMain) { +function createCJSNoSourceModuleWrap(url, parentURL) { debug(`Translating CJSModule without source ${url}`); + const isMain = (parentURL === undefined); const filename = urlToFilename(url); @@ -301,54 +307,60 @@ function createCJSNoSourceModuleWrap(url, isMain) { }, module); } -translators.set('commonjs-sync', function requireCommonJS(url, source, isMain) { +translators.set('commonjs-sync', function requireCommonJS(url, translateContext, parentURL) { initCJSParseSync(); - return createCJSModuleWrap(url, source, isMain, 'commonjs', (module, source, url, filename, isMain) => { - assert(module === CJSModule._cache[filename]); - wrapModuleLoad(filename, null, isMain); - }); + return createCJSModuleWrap(url, translateContext, parentURL, loadCJSModuleWithModuleLoad); }); // Handle CommonJS modules referenced by `require` calls. // This translator function must be sync, as `require` is sync. -translators.set('require-commonjs', (url, source, isMain) => { +translators.set('require-commonjs', (url, translateContext, parentURL) => { initCJSParseSync(); assert(cjsParse); - return createCJSModuleWrap(url, source, isMain, 'commonjs'); + return createCJSModuleWrap(url, translateContext, parentURL); }); // Handle CommonJS modules referenced by `require` calls. // This translator function must be sync, as `require` is sync. -translators.set('require-commonjs-typescript', (url, source, isMain) => { +translators.set('require-commonjs-typescript', (url, translateContext, parentURL) => { assert(cjsParse); - const code = stripTypeScriptModuleTypes(stringify(source), url); - return createCJSModuleWrap(url, code, isMain, 'commonjs-typescript'); + translateContext.source = stripTypeScriptModuleTypes(stringify(translateContext.source), url); + return createCJSModuleWrap(url, translateContext, parentURL); }); +// This goes through Module._load to accommodate monkey-patchers. +function loadCJSModuleWithModuleLoad(module, source, url, filename, isMain) { + assert(module === CJSModule._cache[filename]); + wrapModuleLoad(filename, undefined, isMain); +} + // Handle CommonJS modules referenced by `import` statements or expressions, // or as the initial entry point when the ESM loader handles a CommonJS entry. -translators.set('commonjs', function commonjsStrategy(url, source, isMain) { +translators.set('commonjs', function commonjsStrategy(url, translateContext, parentURL) { if (!cjsParse) { initCJSParseSync(); } // For backward-compatibility, it's possible to return a nullish value for - // CJS source associated with a file: URL. In this case, the source is - // obtained by calling the monkey-patchable CJS loader. - const cjsLoader = source == null ? (module, source, url, filename, isMain) => { - assert(module === CJSModule._cache[filename]); - wrapModuleLoad(filename, undefined, isMain); - } : loadCJSModule; + // CJS source associated with a `file:` URL - that usually means the source is not + // customized (is loaded by default load) or the hook author wants it to be reloaded + // through CJS routine. In this case, the source is obtained by calling the + // monkey-patchable CJS loader. + // TODO(joyeecheung): just use wrapModuleLoad and let the CJS loader + // invoke the off-thread hooks. Use a special parent to avoid invoking in-thread + // hooks twice. + const shouldReloadByCJSLoader = (translateContext.shouldBeReloadedByCJSLoader || translateContext.source == null); + const cjsLoader = shouldReloadByCJSLoader ? loadCJSModuleWithModuleLoad : loadCJSModule; try { // We still need to read the FS to detect the exports. - source ??= readFileSync(new URL(url), 'utf8'); + translateContext.source ??= readFileSync(new URL(url), 'utf8'); } catch { // Continue regardless of error. } - return createCJSModuleWrap(url, source, isMain, 'commonjs', cjsLoader); + return createCJSModuleWrap(url, translateContext, parentURL, cjsLoader); }); /** @@ -373,22 +385,6 @@ function cjsEmplaceModuleCacheEntry(filename, parent) { return cjsMod; } -/** - * Emplace a CJS module cache entry for the given URL. - * @param {string} url The module URL - * @param {CJSModule} parent The parent CJS module - * @returns {CJSModule|undefined} the cached CJS module entry, undefined if url cannot be used to identify a CJS entry. - */ -exports.cjsEmplaceModuleCacheEntryForURL = function cjsEmplaceModuleCacheEntryForURL(url, parent) { - const filename = urlToFilename(url); - if (!filename) { - return; - } - const cjsModule = cjsEmplaceModuleCacheEntry(filename, parent); - cjsCache.set(url, cjsModule); - return cjsModule; -}; - /** * Pre-parses a CommonJS module's exports and re-exports. * @param {string} filename - The filename of the module. @@ -454,8 +450,8 @@ function cjsPreparseModuleExports(filename, source, format) { // Strategy for loading a node builtin CommonJS module that isn't // through normal resolution -translators.set('builtin', function builtinStrategy(url) { - debug(`Translating BuiltinModule ${url}`); +translators.set('builtin', function builtinStrategy(url, translateContext) { + debug(`Translating BuiltinModule ${url}`, translateContext); // Slice 'node:' scheme const id = StringPrototypeSlice(url, 5); const module = loadBuiltinModule(id, url); @@ -468,7 +464,8 @@ translators.set('builtin', function builtinStrategy(url) { }); // Strategy for loading a JSON file -translators.set('json', function jsonStrategy(url, source) { +translators.set('json', function jsonStrategy(url, translateContext) { + let { source } = translateContext; assertBufferSource(source, true, 'load'); debug(`Loading JSONModule ${url}`); const pathname = StringPrototypeStartsWith(url, 'file:') ? @@ -536,10 +533,11 @@ translators.set('json', function jsonStrategy(url, source) { * >} [[Instance]] slot proxy for WebAssembly Module Record */ const wasmInstances = new SafeWeakMap(); -translators.set('wasm', function(url, source) { +translators.set('wasm', function(url, translateContext) { + const { source } = translateContext; assertBufferSource(source, false, 'load'); - debug(`Translating WASMModule ${url}`); + debug(`Translating WASMModule ${url}`, translateContext); let compiled; try { @@ -626,9 +624,10 @@ translators.set('wasm', function(url, source) { }); // Strategy for loading a addon -translators.set('addon', function translateAddon(url, source, isMain) { +translators.set('addon', function translateAddon(url, translateContext, parentURL) { emitExperimentalWarning('Importing addons'); + const { source } = translateContext; // The addon must be loaded from file system with dlopen. Assert // the source is null. if (source !== null) { @@ -639,23 +638,25 @@ translators.set('addon', function translateAddon(url, source, isMain) { source); } - debug(`Translating addon ${url}`); + debug(`Translating addon ${url}`, translateContext); - return createCJSNoSourceModuleWrap(url, isMain); + return createCJSNoSourceModuleWrap(url, parentURL); }); // Strategy for loading a commonjs TypeScript module -translators.set('commonjs-typescript', function(url, source, isMain) { +translators.set('commonjs-typescript', function(url, translateContext, parentURL) { + const { source } = translateContext; assertBufferSource(source, true, 'load'); - const code = stripTypeScriptModuleTypes(stringify(source), url); - debug(`Translating TypeScript ${url}`); - return FunctionPrototypeCall(translators.get('commonjs'), this, url, code, isMain); + debug(`Translating TypeScript ${url}`, translateContext); + translateContext.source = stripTypeScriptModuleTypes(stringify(source), url); + return FunctionPrototypeCall(translators.get('commonjs'), this, url, translateContext, parentURL); }); // Strategy for loading an esm TypeScript module -translators.set('module-typescript', function(url, source, isMain) { +translators.set('module-typescript', function(url, translateContext, parentURL) { + const { source } = translateContext; assertBufferSource(source, true, 'load'); - const code = stripTypeScriptModuleTypes(stringify(source), url); - debug(`Translating TypeScript ${url}`); - return FunctionPrototypeCall(translators.get('module'), this, url, code, isMain); + debug(`Translating TypeScript ${url}`, translateContext); + translateContext.source = stripTypeScriptModuleTypes(stringify(source), url); + return FunctionPrototypeCall(translators.get('module'), this, url, translateContext, parentURL); }); diff --git a/lib/internal/modules/esm/utils.js b/lib/internal/modules/esm/utils.js index 4a4279459341e8..a9076a7ae94128 100644 --- a/lib/internal/modules/esm/utils.js +++ b/lib/internal/modules/esm/utils.js @@ -35,13 +35,8 @@ const { ERR_INVALID_ARG_VALUE, } = require('internal/errors').codes; const { getOptionValue } = require('internal/options'); -const { - loadPreloadModules, - initializeFrozenIntrinsics, -} = require('internal/process/pre_execution'); const { emitExperimentalWarning, - getCWDURL, kEmptyObject, } = require('internal/util'); const assert = require('internal/assert'); @@ -283,15 +278,14 @@ async function importModuleDynamicallyCallback(referrerSymbol, specifier, phase, throw new ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING(); } -let _forceDefaultLoader = false; +let _shouldSpawnLoaderHookWorker = true; /** * Initializes handling of ES modules. - * This is configured during pre-execution. Specifically it's set to true for - * the loader worker in internal/main/worker_thread.js. - * @param {boolean} [forceDefaultLoader] - A boolean indicating disabling custom loaders. + * @param {boolean} [shouldSpawnLoaderHookWorker] Whether the custom loader worker + * should be spawned later. */ -function initializeESM(forceDefaultLoader = false) { - _forceDefaultLoader = forceDefaultLoader; +function initializeESM(shouldSpawnLoaderHookWorker = true) { + _shouldSpawnLoaderHookWorker = shouldSpawnLoaderHookWorker; initializeDefaultConditions(); // Setup per-realm callbacks that locate data or callbacks that we keep // track of for different ESM modules. @@ -300,46 +294,12 @@ function initializeESM(forceDefaultLoader = false) { } /** - * Determine whether custom loaders are disabled and it is forced to use the - * default loader. + * Determine whether the custom loader worker should be spawned when initializing + * the singleton ESM loader. * @returns {boolean} */ -function forceDefaultLoader() { - return _forceDefaultLoader; -} - -/** - * Register module customization hooks. - * @returns {Promise} - */ -async function initializeHooks() { - const customLoaderURLs = getOptionValue('--experimental-loader'); - - const { Hooks } = require('internal/modules/esm/hooks'); - const cascadedLoader = require('internal/modules/esm/loader').getOrInitializeCascadedLoader(); - - const hooks = new Hooks(); - cascadedLoader.setCustomizations(hooks); - - // We need the loader customizations to be set _before_ we start invoking - // `--require`, otherwise loops can happen because a `--require` script - // might call `register(...)` before we've installed ourselves. These - // global values are magically set in `setupUserModules` just for us and - // we call them in the correct order. - // N.B. This block appears here specifically in order to ensure that - // `--require` calls occur before `--loader` ones do. - loadPreloadModules(); - initializeFrozenIntrinsics(); - - const parentURL = getCWDURL().href; - for (let i = 0; i < customLoaderURLs.length; i++) { - await hooks.register( - customLoaderURLs[i], - parentURL, - ); - } - - return hooks; +function shouldSpawnLoaderHookWorker() { + return _shouldSpawnLoaderHookWorker; } /** @@ -375,10 +335,8 @@ function compileSourceTextModule(url, source, cascadedLoader, context = kEmptyOb module.exports = { registerModule, initializeESM, - initializeHooks, getDefaultConditions, getConditionsSet, - loaderWorkerId: 'internal/modules/esm/worker', - forceDefaultLoader, + shouldSpawnLoaderHookWorker, compileSourceTextModule, }; diff --git a/lib/internal/modules/esm/worker.js b/lib/internal/modules/esm/worker.js index 6624b740d0a98b..fa5fa87d20efb1 100644 --- a/lib/internal/modules/esm/worker.js +++ b/lib/internal/modules/esm/worker.js @@ -21,12 +21,63 @@ const { isTypedArray, } = require('util/types'); +const { getOptionValue } = require('internal/options'); +const { + loadPreloadModules, + initializeModuleLoaders, + initializeFrozenIntrinsics, +} = require('internal/process/pre_execution'); const { receiveMessageOnPort } = require('internal/worker/io'); const { WORKER_TO_MAIN_THREAD_NOTIFICATION, } = require('internal/modules/esm/shared_constants'); -const { initializeHooks } = require('internal/modules/esm/utils'); const { isMarkedAsUntransferable } = require('internal/buffer'); +const { getCWDURL } = require('internal/util'); +const { isCascadedLoaderInitialized, getOrInitializeCascadedLoader } = require('internal/modules/esm/loader'); +const { AsyncLoaderHooksOnLoaderHookWorker } = require('internal/modules/esm/hooks'); + +/** + * Register asynchronus module loader customization hooks. This should only be run in the loader + * hooks worker. In a non-loader-hooks thread, if any asynchronous loader hook is registered, the + * ModuleLoader#asyncLoaderHooks are initialized to be AsyncLoaderHooksProxiedToLoaderHookWorker + * which posts the messages to the async loader hook worker thread. + * When no asynchronous loader hook is registered, the loader hook worker is not spawned and module + * loading is entiredly done in-thread. + * @returns {Promise} + */ +async function initializeAsyncLoaderHooksOnLoaderHookWorker() { + const customLoaderURLs = getOptionValue('--experimental-loader'); + + // The worker thread spawned for handling asynchronous loader hooks should not + // further spawn other hook threads or there will be an infinite recursion. + const shouldSpawnLoaderHookWorker = false; + // The worker thread for async loader hooks will preload user modules itself in + // initializeAsyncLoaderHooksOnLoaderHookWorker(). + const shouldPreloadModules = false; + initializeModuleLoaders({ shouldSpawnLoaderHookWorker, shouldPreloadModules }); + + assert(!isCascadedLoaderInitialized(), + 'ModuleLoader should be initialized in initializeAsyncLoaderHooksOnLoaderHookWorker()'); + const asyncLoaderHooks = new AsyncLoaderHooksOnLoaderHookWorker(); + getOrInitializeCascadedLoader(asyncLoaderHooks); + + // We need the async loader hooks to be set _before_ we start invoking + // `--require`, otherwise loops can happen because a `--require` script + // might call `register(...)` before we've installed ourselves. These + // global values are magically set in `initializeModuleLoaders` just for us and + // we call them in the correct order. + // N.B. This block appears here specifically in order to ensure that + // `--require` calls occur before `--loader` ones do. + loadPreloadModules(); + initializeFrozenIntrinsics(); + + const parentURL = getCWDURL().href; + for (let i = 0; i < customLoaderURLs.length; i++) { + await asyncLoaderHooks.register(customLoaderURLs[i], parentURL); + } + + return asyncLoaderHooks; +} /** * Transfers an ArrayBuffer, TypedArray, or DataView to a worker thread. @@ -82,7 +133,7 @@ function wrapMessage(status, body) { } /** - * Initializes a worker thread for a customized module loader. + * Initializes the loader hooks worker thread with customized asynchronous module loading hooks. * @param {SharedArrayBuffer} lock - The lock used to synchronize communication between the worker and the main thread. * @param {MessagePort} syncCommPort - The message port used for synchronous communication between the worker and the * main thread. @@ -90,7 +141,7 @@ function wrapMessage(status, body) { * @returns {Promise} A promise that resolves when the worker thread has been initialized. */ async function customizedModuleWorker(lock, syncCommPort, errorHandler) { - let hooks; + let asyncLoaderHooks; let initializationError; let hasInitializationError = false; @@ -106,9 +157,8 @@ async function customizedModuleWorker(lock, syncCommPort, errorHandler) { }; } - try { - hooks = await initializeHooks(); + asyncLoaderHooks = await initializeAsyncLoaderHooksOnLoaderHookWorker(); } catch (exception) { // If there was an error while parsing and executing a user loader, for example if because a // loader contained a syntax error, then we need to send the error to the main thread so it can @@ -178,7 +228,7 @@ async function customizedModuleWorker(lock, syncCommPort, errorHandler) { // the main thread. let hasError = false; let shouldRemoveGlobalErrorHandler = false; - assert(typeof hooks[method] === 'function'); + assert(typeof asyncLoaderHooks[method] === 'function', `${method} is not implemented in the loader worker`); if (port == null && !hasUncaughtExceptionCaptureCallback()) { // When receiving sync messages, we want to unlock the main thread when there's an exception. process.on('uncaughtException', errorHandler); @@ -198,7 +248,7 @@ async function customizedModuleWorker(lock, syncCommPort, errorHandler) { let response; try { - response = await ReflectApply(hooks[method], hooks, args); + response = await ReflectApply(asyncLoaderHooks[method], asyncLoaderHooks, args); } catch (exception) { hasError = true; response = exception; diff --git a/lib/internal/modules/run_main.js b/lib/internal/modules/run_main.js index 2a9ef56d156808..d337eeca801b9c 100644 --- a/lib/internal/modules/run_main.js +++ b/lib/internal/modules/run_main.js @@ -96,7 +96,7 @@ async function asyncRunEntryPointWithESMLoader(callback) { await cascadedLoader.import(userImports[i], parentURL, kEmptyObject); } } else { - cascadedLoader.forceLoadHooks(); + cascadedLoader.waitForAsyncLoaderHookInitialization(); } await callback(cascadedLoader); } catch (err) { diff --git a/lib/internal/process/pre_execution.js b/lib/internal/process/pre_execution.js index f93b7dae1bca9b..76ec7d821cb4cc 100644 --- a/lib/internal/process/pre_execution.js +++ b/lib/internal/process/pre_execution.js @@ -47,6 +47,8 @@ function prepareMainThreadExecution(expandArgv1 = false, initializeModules = tru expandArgv1, initializeModules, isMainThread: true, + shouldSpawnLoaderHookWorker: initializeModules, + shouldPreloadModules: initializeModules, }); } @@ -55,15 +57,20 @@ function prepareTestRunnerMainExecution(loadUserModules = true) { expandArgv1: false, initializeModules: true, isMainThread: true, - forceDefaultLoader: !loadUserModules, + shouldSpawnLoaderHookWorker: loadUserModules, + shouldPreloadModules: loadUserModules, }); } function prepareWorkerThreadExecution() { prepareExecution({ expandArgv1: false, - initializeModules: false, isMainThread: false, + // Module loader initialization in workers are delayed until the worker thread + // is ready for execution. + initializeModules: false, + shouldSpawnLoaderHookWorker: false, + shouldPreloadModules: false, }); } @@ -74,7 +81,7 @@ function prepareShadowRealmExecution() { setupDebugEnv(); // Disable custom loaders in ShadowRealm. - setupUserModules(true); + initializeModuleLoaders({ shouldSpawnLoaderHookWorker: false, shouldPreloadModules: false }); const { privateSymbols: { host_defined_option_symbol, @@ -96,7 +103,7 @@ function prepareShadowRealmExecution() { } function prepareExecution(options) { - const { expandArgv1, initializeModules, isMainThread, forceDefaultLoader } = options; + const { expandArgv1, initializeModules, isMainThread, shouldSpawnLoaderHookWorker, shouldPreloadModules } = options; refreshRuntimeOptions(); @@ -154,8 +161,9 @@ function prepareExecution(options) { assert(!initializeModules); } + setupVmModules(); if (initializeModules) { - setupUserModules(forceDefaultLoader); + initializeModuleLoaders({ shouldSpawnLoaderHookWorker, shouldPreloadModules }); } // This has to be done after the user module loader is initialized, @@ -165,6 +173,21 @@ function prepareExecution(options) { return mainEntry; } +function setupVmModules() { + // Patch the vm module when --experimental-vm-modules is on. + // Please update the comments in vm.js when this block changes. + // TODO(joyeecheung): move this to vm.js? + if (getOptionValue('--experimental-vm-modules')) { + const { + Module, SourceTextModule, SyntheticModule, + } = require('internal/vm/module'); + const vm = require('vm'); + vm.Module = Module; + vm.SourceTextModule = SourceTextModule; + vm.SyntheticModule = SyntheticModule; + } +} + function setupHttpProxy() { // This normalized from both --use-env-proxy and NODE_USE_ENV_PROXY settings. if (!getOptionValue('--use-env-proxy')) { @@ -186,22 +209,32 @@ function setupHttpProxy() { // existing libraries that sets the global dispatcher or monkey patches the global agent. } -function setupUserModules(forceDefaultLoader = false) { - initializeCJSLoader(); - initializeESMLoader(forceDefaultLoader); +function initializeModuleLoaders(options) { + const { shouldSpawnLoaderHookWorker, shouldPreloadModules } = options; + // Initialize certain special module.Module properties and the CJS conditions. + const { initializeCJS } = require('internal/modules/cjs/loader'); + initializeCJS(); + // Initialize the ESM loader and a few module callbacks. + // If shouldSpawnLoaderHookWorker is true, later when the ESM loader is instantiated on-demand, + // it will spawn a loader worker thread to handle async custom loader hooks. + const { initializeESM } = require('internal/modules/esm/utils'); + initializeESM(shouldSpawnLoaderHookWorker); + const { hasStartedUserCJSExecution, hasStartedUserESMExecution, } = require('internal/modules/helpers'); + // At this point, no user module has been executed yet. assert(!hasStartedUserCJSExecution()); assert(!hasStartedUserESMExecution()); + if (getEmbedderOptions().hasEmbedderPreload) { runEmbedderPreload(); } // Do not enable preload modules if custom loaders are disabled. // For example, loader workers are responsible for doing this themselves. // And preload modules are not supported in ShadowRealm as well. - if (!forceDefaultLoader) { + if (shouldPreloadModules) { loadPreloadModules(); } // Need to be done after --require setup. @@ -638,28 +671,6 @@ function initializePermission() { } } -function initializeCJSLoader() { - const { initializeCJS } = require('internal/modules/cjs/loader'); - initializeCJS(); -} - -function initializeESMLoader(forceDefaultLoader) { - const { initializeESM } = require('internal/modules/esm/utils'); - initializeESM(forceDefaultLoader); - - // Patch the vm module when --experimental-vm-modules is on. - // Please update the comments in vm.js when this block changes. - if (getOptionValue('--experimental-vm-modules')) { - const { - Module, SourceTextModule, SyntheticModule, - } = require('internal/vm/module'); - const vm = require('vm'); - vm.Module = Module; - vm.SourceTextModule = SourceTextModule; - vm.SyntheticModule = SyntheticModule; - } -} - function initializeSourceMapsHandlers() { const { setSourceMapsSupport, @@ -729,7 +740,7 @@ function getHeapSnapshotFilename(diagnosticDir) { } module.exports = { - setupUserModules, + initializeModuleLoaders, prepareMainThreadExecution, prepareWorkerThreadExecution, prepareShadowRealmExecution, diff --git a/lib/internal/test_runner/coverage.js b/lib/internal/test_runner/coverage.js index 80811dffb1e1d0..8fa9c872568d1e 100644 --- a/lib/internal/test_runner/coverage.js +++ b/lib/internal/test_runner/coverage.js @@ -37,7 +37,7 @@ const { }, } = require('internal/errors'); const { matchGlobPattern } = require('internal/fs/glob'); -const { kMockSearchParam } = require('internal/test_runner/mock/mock'); +const { constants: { kMockSearchParam } } = require('internal/test_runner/mock/loader'); const kCoverageFileRegex = /^coverage-(\d+)-(\d{13})-(\d+)\.json$/; const kIgnoreRegex = /\/\* node:coverage ignore next (?\d+ )?\*\//; diff --git a/lib/internal/test_runner/mock/loader.js b/lib/internal/test_runner/mock/loader.js index ddde5599df509a..a7a22539be3093 100644 --- a/lib/internal/test_runner/mock/loader.js +++ b/lib/internal/test_runner/mock/loader.js @@ -1,77 +1,24 @@ 'use strict'; const { - AtomicsNotify, - AtomicsStore, JSONStringify, SafeMap, } = primordials; -const { - kBadExportsMessage, - kMockSearchParam, - kMockSuccess, - kMockExists, - kMockUnknownMessage, -} = require('internal/test_runner/mock/mock'); + +const kMockSearchParam = 'node-test-mock'; +const kBadExportsMessage = 'Cannot create mock because named exports ' + + 'cannot be applied to the provided default export.'; + const { URL, URLParse } = require('internal/url'); let debug = require('internal/util/debuglog').debuglog('test_runner', (fn) => { debug = fn; }); -// TODO(cjihrig): The mocks need to be thread aware because the exports are -// evaluated on the thread that creates the mock. Before marking this API as -// stable, one of the following issues needs to be implemented: -// https://github.com/nodejs/node/issues/49472 -// or https://github.com/nodejs/node/issues/52219 - const mocks = new SafeMap(); -async function initialize(data) { - data?.port.on('message', ({ type, payload }) => { - debug('mock loader received message type "%s" with payload %o', type, payload); - - if (type === 'node:test:register') { - const { baseURL } = payload; - const mock = mocks.get(baseURL); - - if (mock?.active) { - debug('already mocking "%s"', baseURL); - sendAck(payload.ack, kMockExists); - return; - } - - const localVersion = mock?.localVersion ?? 0; - - debug('new mock version %d for "%s"', localVersion, baseURL); - mocks.set(baseURL, { - __proto__: null, - active: true, - cache: payload.cache, - exportNames: payload.exportNames, - format: payload.format, - hasDefaultExport: payload.hasDefaultExport, - localVersion, - url: baseURL, - }); - sendAck(payload.ack); - } else if (type === 'node:test:unregister') { - const mock = mocks.get(payload.baseURL); - - if (mock !== undefined) { - mock.active = false; - mock.localVersion++; - } - - sendAck(payload.ack); - } else { - sendAck(payload.ack, kMockUnknownMessage); - } - }); -} - -async function resolve(specifier, context, nextResolve) { +function resolve(specifier, context, nextResolve) { debug('resolve hook entry, specifier = "%s", context = %o', specifier, context); - const nextResolveResult = await nextResolve(specifier, context); + const nextResolveResult = nextResolve(specifier, context); const mockSpecifier = nextResolveResult.url; const mock = mocks.get(mockSpecifier); @@ -95,7 +42,7 @@ async function resolve(specifier, context, nextResolve) { return { __proto__: null, url: href, format: nextResolveResult.format }; } -async function load(url, context, nextLoad) { +function load(url, context, nextLoad) { debug('load hook entry, url = "%s", context = %o', url, context); const parsedURL = URLParse(url); if (parsedURL) { @@ -105,7 +52,7 @@ async function load(url, context, nextLoad) { const baseURL = parsedURL ? parsedURL.href : url; const mock = mocks.get(baseURL); - const original = await nextLoad(url, context); + const original = nextLoad(url, context); debug('load hook, mock = %o', mock); if (mock?.active !== true) { return original; @@ -130,14 +77,14 @@ async function load(url, context, nextLoad) { __proto__: null, format, shortCircuit: true, - source: await createSourceFromMock(mock, format), + source: createSourceFromMock(mock, format), }; debug('load hook finished, result = %o', result); return result; } -async function createSourceFromMock(mock, format) { +function createSourceFromMock(mock, format) { // Create mock implementation from provided exports. const { exportNames, hasDefaultExport, url } = mock; const useESM = format === 'module' || format === 'module-typescript'; @@ -196,9 +143,12 @@ if (module.exports === null || typeof module.exports !== 'object') { return source; } -function sendAck(buf, status = kMockSuccess) { - AtomicsStore(buf, 0, status); - AtomicsNotify(buf, 0); -} - -module.exports = { initialize, load, resolve }; +module.exports = { + hooks: { __proto__: null, load, resolve }, + mocks, + constants: { + __proto__: null, + kBadExportsMessage, + kMockSearchParam, + }, +}; diff --git a/lib/internal/test_runner/mock/mock.js b/lib/internal/test_runner/mock/mock.js index 8a63c7757d1752..fac97e51b6a2cf 100644 --- a/lib/internal/test_runner/mock/mock.js +++ b/lib/internal/test_runner/mock/mock.js @@ -2,12 +2,9 @@ const { ArrayPrototypePush, ArrayPrototypeSlice, - AtomicsStore, - AtomicsWait, Error, FunctionPrototypeBind, FunctionPrototypeCall, - Int32Array, ObjectDefineProperty, ObjectGetOwnPropertyDescriptor, ObjectGetPrototypeOf, @@ -19,9 +16,6 @@ const { SafeMap, StringPrototypeSlice, StringPrototypeStartsWith, - globalThis: { - SharedArrayBuffer, - }, } = primordials; const { codes: { @@ -54,19 +48,10 @@ const { validateOneOf, } = require('internal/validators'); const { MockTimers } = require('internal/test_runner/mock/mock_timers'); -const { strictEqual, notStrictEqual } = require('assert'); const { Module } = require('internal/modules/cjs/loader'); -const { MessageChannel } = require('worker_threads'); const { _load, _nodeModulePaths, _resolveFilename, isBuiltin } = Module; function kDefaultFunction() {} const enableModuleMocking = getOptionValue('--experimental-test-module-mocks'); -const kMockSearchParam = 'node-test-mock'; -const kMockSuccess = 1; -const kMockExists = 2; -const kMockUnknownMessage = 3; -const kWaitTimeout = 5_000; -const kBadExportsMessage = 'Cannot create mock because named exports ' + - 'cannot be applied to the provided default export.'; const kSupportedFormats = [ 'builtin', 'commonjs-typescript', @@ -76,6 +61,11 @@ const kSupportedFormats = [ 'module', ]; let sharedModuleState; +const { + hooks: mockHooks, + mocks, + constants: { kBadExportsMessage, kMockSearchParam }, +} = require('internal/test_runner/mock/loader'); class MockFunctionContext { #calls; @@ -201,8 +191,8 @@ class MockModuleContext { hasDefaultExport, namedExports, sharedState, + specifier, }) { - const ack = new Int32Array(new SharedArrayBuffer(4)); const config = { __proto__: null, cache, @@ -218,7 +208,6 @@ class MockModuleContext { this.#sharedState = sharedState; this.#restore = { __proto__: null, - ack, baseURL, cached: fullPath in Module._cache, format, @@ -226,20 +215,29 @@ class MockModuleContext { value: Module._cache[fullPath], }; - sharedState.loaderPort.postMessage({ - __proto__: null, - type: 'node:test:register', - payload: { + const mock = mocks.get(baseURL); + + if (mock?.active) { + debug('already mocking "%s"', baseURL); + throw new ERR_INVALID_STATE( + `Cannot mock '${specifier}'. The module is already mocked.`, + ); + } else { + const localVersion = mock?.localVersion ?? 0; + + debug('new mock version %d for "%s"', localVersion, baseURL); + mocks.set(baseURL, { __proto__: null, - ack, - baseURL, + url: baseURL, cache, exportNames: ObjectKeys(namedExports), hasDefaultExport, format, - }, - }); - waitForAck(ack); + localVersion, + active: true, + }); + } + delete Module._cache[fullPath]; sharedState.mockExports.set(baseURL, { __proto__: null, @@ -261,17 +259,12 @@ class MockModuleContext { Module._cache[this.#restore.fullPath] = this.#restore.value; } - AtomicsStore(this.#restore.ack, 0, 0); - this.#sharedState.loaderPort.postMessage({ - __proto__: null, - type: 'node:test:unregister', - payload: { - __proto__: null, - ack: this.#restore.ack, - baseURL: this.#restore.baseURL, - }, - }); - waitForAck(this.#restore.ack); + const mock = mocks.get(this.#restore.baseURL); + + if (mock !== undefined) { + mock.active = false; + mock.localVersion++; + } this.#sharedState.mockMap.delete(this.#restore.baseURL); this.#sharedState.mockMap.delete(this.#restore.fullPath); @@ -654,7 +647,7 @@ class MockTracker { const hasFileProtocol = StringPrototypeStartsWith(filename, 'file://'); const caller = hasFileProtocol ? filename : pathToFileURL(filename).href; const { format, url } = sharedState.moduleLoader.resolveSync( - mockSpecifier, caller, null, + mockSpecifier, caller, kEmptyObject, ); debug('module mock, url = "%s", format = "%s", caller = "%s"', url, format, caller); if (format) { // Format is not yet known for ambiguous files when detection is enabled. @@ -828,20 +821,13 @@ function setupSharedModuleState() { if (sharedModuleState === undefined) { const { mock } = require('test'); const mockExports = new SafeMap(); - const { port1, port2 } = new MessageChannel(); + const { registerHooks } = require('internal/modules/customization_hooks'); const moduleLoader = esmLoader.getOrInitializeCascadedLoader(); - moduleLoader.register( - 'internal/test_runner/mock/loader', - 'node:', - { __proto__: null, port: port2 }, - [port2], - true, - ); + registerHooks(mockHooks); sharedModuleState = { __proto__: null, - loaderPort: port1, mockExports, mockMap: new SafeMap(), moduleLoader, @@ -941,13 +927,6 @@ function findMethodOnPrototypeChain(instance, methodName) { return descriptor; } -function waitForAck(buf) { - const result = AtomicsWait(buf, 0, 0, kWaitTimeout); - - notStrictEqual(result, 'timed-out', 'test mocking synchronization failed'); - strictEqual(buf[0], kMockSuccess); -} - function ensureNodeScheme(specifier) { if (!StringPrototypeStartsWith(specifier, 'node:')) { return `node:${specifier}`; @@ -962,10 +941,5 @@ if (!enableModuleMocking) { module.exports = { ensureNodeScheme, - kBadExportsMessage, - kMockSearchParam, - kMockSuccess, - kMockExists, - kMockUnknownMessage, MockTracker, }; diff --git a/lib/internal/tls/common.js b/lib/internal/tls/common.js index 66331d2d9999e9..0f7fd0747ea779 100644 --- a/lib/internal/tls/common.js +++ b/lib/internal/tls/common.js @@ -82,10 +82,11 @@ function SecureContext(secureProtocol, secureOptions, minVersion, maxVersion) { throw new ERR_TLS_PROTOCOL_VERSION_CONFLICT(maxVersion, secureProtocol); } + const minV = toV('minimum', minVersion, tls.DEFAULT_MIN_VERSION); + const maxV = toV('maximum', maxVersion, tls.DEFAULT_MAX_VERSION); + this.context = new NativeSecureContext(); - this.context.init(secureProtocol, - toV('minimum', minVersion, tls.DEFAULT_MIN_VERSION), - toV('maximum', maxVersion, tls.DEFAULT_MAX_VERSION)); + this.context.init(secureProtocol, minV, maxV); if (secureOptions) { validateInteger(secureOptions, 'secureOptions'); diff --git a/lib/internal/util.js b/lib/internal/util.js index 6f9c43c26f0dc9..0b1fa03cf1fab0 100644 --- a/lib/internal/util.js +++ b/lib/internal/util.js @@ -8,6 +8,7 @@ const { Error, ErrorCaptureStackTrace, FunctionPrototypeCall, + FunctionPrototypeSymbolHasInstance, NumberParseInt, ObjectDefineProperties, ObjectDefineProperty, @@ -96,7 +97,7 @@ function isError(e) { // An error could be an instance of Error while not being a native error // or could be from a different realm and not be instance of Error but still // be a native error. - return isNativeError(e) || e instanceof Error; + return isNativeError(e) || FunctionPrototypeSymbolHasInstance(Error, e); } // Keep a list of deprecation codes that have been warned on so we only warn on diff --git a/lib/internal/util/inspect.js b/lib/internal/util/inspect.js index d27b7df778f100..a00355b2dc21aa 100644 --- a/lib/internal/util/inspect.js +++ b/lib/internal/util/inspect.js @@ -72,6 +72,7 @@ const { ObjectPrototype, ObjectPrototypeHasOwnProperty, ObjectPrototypePropertyIsEnumerable, + ObjectPrototypeToString, ObjectSeal, ObjectSetPrototypeOf, Promise, @@ -111,7 +112,6 @@ const { StringPrototypeSplit, StringPrototypeStartsWith, StringPrototypeToLowerCase, - StringPrototypeTrim, StringPrototypeValueOf, SymbolIterator, SymbolPrototypeToString, @@ -1223,6 +1223,7 @@ function formatRaw(ctx, value, recurseTimes, typedArray) { const filter = ctx.showHidden ? ALL_PROPERTIES : ONLY_ENUMERABLE; let extrasType = kObjectType; + let extraKeys; // Iterators and the rest are split to reduce checks. // We have to check all values in case the constructor is set to null. @@ -1278,6 +1279,11 @@ function formatRaw(ctx, value, recurseTimes, typedArray) { // bound function is required to reconstruct missing information. formatter = FunctionPrototypeBind(formatTypedArray, null, bound, size); extrasType = kArrayExtrasType; + + if (ctx.showHidden) { + extraKeys = ['BYTES_PER_ELEMENT', 'length', 'byteLength', 'byteOffset', 'buffer']; + typedArray = true; + } } else if (isMapIterator(value)) { keys = getKeys(value, ctx.showHidden); braces = getIteratorBraces('Map', tag); @@ -1347,14 +1353,14 @@ function formatRaw(ctx, value, recurseTimes, typedArray) { formatter = formatArrayBuffer; } else if (keys.length === 0 && protoProps === undefined) { return prefix + - `{ byteLength: ${formatNumber(ctx.stylize, value.byteLength, false)} }`; + `{ [byteLength]: ${formatNumber(ctx.stylize, value.byteLength, false)} }`; } braces[0] = `${prefix}{`; - ArrayPrototypeUnshift(keys, 'byteLength'); + extraKeys = ['byteLength']; } else if (isDataView(value)) { braces[0] = `${getPrefix(constructor, tag, 'DataView')}{`; // .buffer goes last, it's not a primitive like the others. - ArrayPrototypeUnshift(keys, 'byteLength', 'byteOffset', 'buffer'); + extraKeys = ['byteLength', 'byteOffset', 'buffer']; } else if (isPromise(value)) { braces[0] = `${getPrefix(constructor, tag, 'Promise')}{`; formatter = formatPromise; @@ -1404,6 +1410,18 @@ function formatRaw(ctx, value, recurseTimes, typedArray) { const indentationLvl = ctx.indentationLvl; try { output = formatter(ctx, value, recurseTimes); + if (extraKeys !== undefined) { + for (i = 0; i < extraKeys.length; i++) { + let formatted; + try { + formatted = formatExtraProperties(ctx, value, recurseTimes, extraKeys[i], typedArray); + } catch { + const tempValue = { [extraKeys[i]]: value.buffer[extraKeys[i]] }; + formatted = formatExtraProperties(ctx, tempValue, recurseTimes, extraKeys[i], typedArray); + } + ArrayPrototypePush(output, formatted); + } + } for (i = 0; i < keys.length; i++) { ArrayPrototypePush( output, @@ -1703,13 +1721,19 @@ function getDuplicateErrorFrameRanges(frames) { } function getStackString(ctx, error) { - if (error.stack) { - if (typeof error.stack === 'string') { - return error.stack; + let stack; + try { + stack = error.stack; + } catch { + // If stack is getter that throws, we ignore the error. + } + if (stack) { + if (typeof stack === 'string') { + return stack; } ctx.seen.push(error); ctx.indentationLvl += 4; - const result = formatValue(ctx, error.stack); + const result = formatValue(ctx, stack); ctx.indentationLvl -= 4; ctx.seen.pop(); return `${ErrorPrototypeToString(error)}\n ${result}`; @@ -1805,18 +1829,6 @@ function improveStack(stack, constructor, name, tag) { return stack; } -function removeDuplicateErrorKeys(ctx, keys, err, stack) { - if (!ctx.showHidden && keys.length !== 0) { - for (const name of ['name', 'message', 'stack']) { - const index = ArrayPrototypeIndexOf(keys, name); - // Only hide the property if it's a string and if it's part of the original stack - if (index !== -1 && (typeof err[name] !== 'string' || StringPrototypeIncludes(stack, err[name]))) { - ArrayPrototypeSplice(keys, index, 1); - } - } - } -} - function markNodeModules(ctx, line) { let tempLine = ''; let lastPos = 0; @@ -1899,10 +1911,49 @@ function safeGetCWD() { } function formatError(err, constructor, tag, ctx, keys) { - const name = err.name != null ? err.name : 'Error'; - let stack = getStackString(ctx, err); + let message, name, stack; + try { + stack = getStackString(ctx, err); + } catch { + return ObjectPrototypeToString(err); + } + + let messageIsGetterThatThrows = false; + try { + message = err.message; + } catch { + messageIsGetterThatThrows = true; + } + let nameIsGetterThatThrows = false; + try { + name = err.name; + } catch { + nameIsGetterThatThrows = true; + } - removeDuplicateErrorKeys(ctx, keys, err, stack); + if (!ctx.showHidden && keys.length !== 0) { + const index = ArrayPrototypeIndexOf(keys, 'stack'); + if (index !== -1) { + ArrayPrototypeSplice(keys, index, 1); + } + + if (!messageIsGetterThatThrows) { + const index = ArrayPrototypeIndexOf(keys, 'message'); + // Only hide the property if it's a string and if it's part of the original stack + if (index !== -1 && (typeof message !== 'string' || StringPrototypeIncludes(stack, message))) { + ArrayPrototypeSplice(keys, index, 1); + } + } + + if (!nameIsGetterThatThrows) { + const index = ArrayPrototypeIndexOf(keys, 'name'); + // Only hide the property if it's a string and if it's part of the original stack + if (index !== -1 && (typeof name !== 'string' || StringPrototypeIncludes(stack, name))) { + ArrayPrototypeSplice(keys, index, 1); + } + } + } + name ??= 'Error'; if ('cause' in err && (keys.length === 0 || !ArrayPrototypeIncludes(keys, 'cause'))) { @@ -1910,17 +1961,22 @@ function formatError(err, constructor, tag, ctx, keys) { } // Print errors aggregated into AggregateError - if (ArrayIsArray(err.errors) && + try { + const errors = err.errors; + if (ArrayIsArray(errors) && (keys.length === 0 || !ArrayPrototypeIncludes(keys, 'errors'))) { - ArrayPrototypePush(keys, 'errors'); + ArrayPrototypePush(keys, 'errors'); + } + } catch { + // If errors is a getter that throws, we ignore the error. } stack = improveStack(stack, constructor, name, tag); // Ignore the error message if it's contained in the stack. - let pos = (err.message && StringPrototypeIndexOf(stack, err.message)) || -1; + let pos = (message && StringPrototypeIndexOf(stack, message)) || -1; if (pos !== -1) - pos += err.message.length; + pos += message.length; // Wrap the error in brackets in case it has no stack trace. const stackStart = StringPrototypeIndexOf(stack, '\n at', pos); if (stackStart === -1) { @@ -2263,11 +2319,15 @@ function formatArrayBuffer(ctx, value) { } if (hexSlice === undefined) hexSlice = uncurryThis(require('buffer').Buffer.prototype.hexSlice); - let str = StringPrototypeTrim(RegExpPrototypeSymbolReplace( - /(.{2})/g, - hexSlice(buffer, 0, MathMin(ctx.maxArrayLength, buffer.length)), - '$1 ', - )); + const rawString = hexSlice(buffer, 0, MathMin(ctx.maxArrayLength, buffer.length)); + let str = ''; + let i = 0; + for (; i < rawString.length - 2; i += 2) { + str += `${rawString[i]}${rawString[i + 1]} `; + } + if (rawString.length > 0) { + str += `${rawString[i]}${rawString[i + 1]}`; + } const remaining = buffer.length - ctx.maxArrayLength; if (remaining > 0) str += ` ... ${remaining} more byte${remaining > 1 ? 's' : ''}`; @@ -2294,7 +2354,7 @@ function formatArray(ctx, value, recurseTimes) { return output; } -function formatTypedArray(value, length, ctx, ignored, recurseTimes) { +function formatTypedArray(value, length, ctx) { const maxLength = MathMin(MathMax(0, ctx.maxArrayLength), length); const remaining = value.length - maxLength; const output = new Array(maxLength); @@ -2307,22 +2367,6 @@ function formatTypedArray(value, length, ctx, ignored, recurseTimes) { if (remaining > 0) { output[maxLength] = remainingText(remaining); } - if (ctx.showHidden) { - // .buffer goes last, it's not a primitive like the others. - // All besides `BYTES_PER_ELEMENT` are actually getters. - ctx.indentationLvl += 2; - for (const key of [ - 'BYTES_PER_ELEMENT', - 'length', - 'byteLength', - 'byteOffset', - 'buffer', - ]) { - const str = formatValue(ctx, value[key], recurseTimes, true); - ArrayPrototypePush(output, `[${key}]: ${str}`); - } - ctx.indentationLvl -= 2; - } return output; } @@ -2470,12 +2514,20 @@ function formatPromise(ctx, value, recurseTimes) { return output; } +function formatExtraProperties(ctx, value, recurseTimes, key, typedArray) { + ctx.indentationLvl += 2; + const str = formatValue(ctx, value[key], recurseTimes, typedArray); + ctx.indentationLvl -= 2; + + // These entries are mainly getters. Should they be formatted like getters? + return ctx.stylize(`[${key}]: ${str}`, 'string'); +} + function formatProperty(ctx, value, recurseTimes, key, type, desc, original = value) { let name, str; let extra = ' '; - desc ||= ObjectGetOwnPropertyDescriptor(value, key) || - { value: value[key], enumerable: true }; + desc ??= ObjectGetOwnPropertyDescriptor(value, key); if (desc.value !== undefined) { const diff = (ctx.compact !== true || type !== kObjectType) ? 2 : 3; ctx.indentationLvl += diff; diff --git a/lib/internal/v8/startup_snapshot.js b/lib/internal/v8/startup_snapshot.js index 01204b96239406..af5fb07925dc15 100644 --- a/lib/internal/v8/startup_snapshot.js +++ b/lib/internal/v8/startup_snapshot.js @@ -115,8 +115,8 @@ function setDeserializeMainFunction(callback, data) { }); } +initializeCallbacks(); module.exports = { - initializeCallbacks, runDeserializeCallbacks, throwIfBuildingSnapshot, // Exposed to require('v8').startupSnapshot diff --git a/lib/internal/vm/module.js b/lib/internal/vm/module.js index 457d26d27692a9..6403a1f76710f9 100644 --- a/lib/internal/vm/module.js +++ b/lib/internal/vm/module.js @@ -13,6 +13,7 @@ const { ObjectPrototypeHasOwnProperty, ObjectSetPrototypeOf, PromisePrototypeThen, + PromiseReject, PromiseResolve, ReflectApply, SafePromiseAllReturnArrayLike, @@ -208,27 +209,31 @@ class Module { this[kWrap].instantiate(); } - async evaluate(options = kEmptyObject) { - validateThisInternalField(this, kWrap, 'Module'); - validateObject(options, 'options'); - - let timeout = options.timeout; - if (timeout === undefined) { - timeout = -1; - } else { - validateUint32(timeout, 'options.timeout', true); - } - const { breakOnSigint = false } = options; - validateBoolean(breakOnSigint, 'options.breakOnSigint'); - const status = this[kWrap].getStatus(); - if (status !== kInstantiated && - status !== kEvaluated && - status !== kErrored) { - throw new ERR_VM_MODULE_STATUS( - 'must be one of linked, evaluated, or errored', - ); + evaluate(options = kEmptyObject) { + try { + validateThisInternalField(this, kWrap, 'Module'); + validateObject(options, 'options'); + + let timeout = options.timeout; + if (timeout === undefined) { + timeout = -1; + } else { + validateUint32(timeout, 'options.timeout', true); + } + const { breakOnSigint = false } = options; + validateBoolean(breakOnSigint, 'options.breakOnSigint'); + const status = this[kWrap].getStatus(); + if (status !== kInstantiated && + status !== kEvaluated && + status !== kErrored) { + throw new ERR_VM_MODULE_STATUS( + 'must be one of linked, evaluated, or errored', + ); + } + return this[kWrap].evaluate(timeout, breakOnSigint); + } catch (e) { + return PromiseReject(e); } - await this[kWrap].evaluate(timeout, breakOnSigint); } [customInspectSymbol](depth, options) { diff --git a/node.gyp b/node.gyp index e22dce67ebf6c7..2f4934b9a8c8b8 100644 --- a/node.gyp +++ b/node.gyp @@ -425,6 +425,7 @@ 'test/cctest/test_quic_tokens.cc', ], 'node_cctest_inspector_sources': [ + 'test/cctest/inspector/test_network_requests_buffer.cc', 'test/cctest/inspector/test_node_protocol.cc', 'test/cctest/test_inspector_socket.cc', 'test/cctest/test_inspector_socket_server.cc', @@ -490,6 +491,19 @@ ['clang==0 and OS!="win"', { 'cflags': [ '-Wno-restrict', ], }], + # TODO(joyeecheung): investigate if it breaks addons. + # ['OS=="mac"', { + # 'xcode_settings': { + # 'GCC_SYMBOLS_PRIVATE_EXTERN': 'YES', # -fvisibility=hidden + # 'GCC_INLINES_ARE_PRIVATE_EXTERN': 'YES' # -fvisibility-inlines-hidden + # }, + # }], + # ['OS!="win" or clang==1', { + # 'cflags': [ + # '-fvisibility=hidden', + # '-fvisibility-inlines-hidden' + # ], + # }], # Pointer authentication for ARM64. ['target_arch=="arm64"', { 'target_conditions': [ diff --git a/shell.nix b/shell.nix new file mode 100644 index 00000000000000..5b66e922d12ca8 --- /dev/null +++ b/shell.nix @@ -0,0 +1,120 @@ +{ + pkgs ? import "${./tools/nix/pkgs.nix}" { }, + loadJSBuiltinsDynamically ? true, # Load `lib/**.js` from disk instead of embedding + ncu-path ? null, # Provide this if you want to use a local version of NCU + icu ? pkgs.icu, + sharedLibDeps ? { + inherit (pkgs) + ada + brotli + c-ares + libuv + nghttp2 + nghttp3 + ngtcp2 + openssl + simdjson + simdutf + sqlite + uvwasi + zlib + zstd + ; + http-parser = pkgs.llhttp; + }, + ccache ? pkgs.ccache, + ninja ? pkgs.ninja, + devTools ? [ + pkgs.curl + pkgs.gh + pkgs.git + pkgs.jq + pkgs.shellcheck + ] + ++ ( + if (ncu-path == null) then + [ pkgs.node-core-utils ] + else + [ + (pkgs.writeShellScriptBin "git-node" "exec \"${ncu-path}/bin/git-node.js\" \"$@\"") + (pkgs.writeShellScriptBin "ncu-ci" "exec \"${ncu-path}/bin/ncu-ci.js\" \"$@\"") + (pkgs.writeShellScriptBin "ncu-config" "exec \"${ncu-path}/bin/ncu-config.js\" \"$@\"") + ] + ), + benchmarkTools ? [ + pkgs.R + pkgs.rPackages.ggplot2 + pkgs.rPackages.plyr + pkgs.wrk + ], + extraConfigFlags ? [ + "--without-npm" + "--debug-node" + ], +}: + +let + useSharedICU = if builtins.isString icu then icu == "system" else icu != null; + useSharedAda = builtins.hasAttr "ada" sharedLibDeps; + useSharedOpenSSL = builtins.hasAttr "openssl" sharedLibDeps; +in +pkgs.mkShell { + inherit (pkgs.nodejs_latest) nativeBuildInputs; + + buildInputs = builtins.attrValues sharedLibDeps ++ pkgs.lib.optionals useSharedICU [ icu ]; + + packages = [ + ccache + ] + ++ devTools + ++ benchmarkTools; + + shellHook = + if (ccache != null) then + '' + export CC="${pkgs.lib.getExe ccache} $CC" + export CXX="${pkgs.lib.getExe ccache} $CXX" + '' + else + ""; + + BUILD_WITH = if (ninja != null) then "ninja" else "make"; + NINJA = if (ninja != null) then "${pkgs.lib.getExe ninja}" else ""; + CI_SKIP_TESTS = pkgs.lib.concatStringsSep "," ( + [ ] + ++ pkgs.lib.optionals useSharedAda [ + # Different versions of Ada affect the WPT tests + "test-url" + ] + ++ pkgs.lib.optionals useSharedOpenSSL [ + # Path to the openssl.cnf is different from the expected one + "test-strace-openat-openssl" + ] + ); + CONFIG_FLAGS = builtins.toString ( + [ + ( + if icu == null then + "--without-intl" + else + "--with-intl=${if useSharedICU then "system" else icu}-icu" + ) + ] + ++ extraConfigFlags + ++ pkgs.lib.optionals (ninja != null) [ + "--ninja" + ] + ++ pkgs.lib.optionals loadJSBuiltinsDynamically [ + "--node-builtin-modules-path=${builtins.toString ./.}" + ] + ++ pkgs.lib.concatMap (name: [ + "--shared-${builtins.replaceStrings [ "c-ares" ] [ "cares" ] name}" + "--shared-${builtins.replaceStrings [ "c-ares" ] [ "cares" ] name}-libpath=${ + pkgs.lib.getLib sharedLibDeps.${name} + }/lib" + "--shared-${builtins.replaceStrings [ "c-ares" ] [ "cares" ] name}-include=${ + pkgs.lib.getInclude sharedLibDeps.${name} + }/include" + ]) (builtins.attrNames sharedLibDeps) + ); +} diff --git a/src/api/async_resource.cc b/src/api/async_resource.cc index 38d53a9aec0e58..621fca4ba4d1da 100644 --- a/src/api/async_resource.cc +++ b/src/api/async_resource.cc @@ -16,6 +16,13 @@ AsyncResource::AsyncResource(Isolate* isolate, Local resource, const char* name, async_id trigger_async_id) + : AsyncResource( + isolate, resource, std::string_view(name), trigger_async_id) {} + +AsyncResource::AsyncResource(Isolate* isolate, + Local resource, + std::string_view name, + async_id trigger_async_id) : env_(Environment::GetCurrent(isolate)), resource_(isolate, resource), context_frame_(isolate, async_context_frame::current(isolate)) { diff --git a/src/api/embed_helpers.cc b/src/api/embed_helpers.cc index f6ad46dae3db9a..c1bcb3948c4cf6 100644 --- a/src/api/embed_helpers.cc +++ b/src/api/embed_helpers.cc @@ -1,6 +1,7 @@ #include "debug_utils-inl.h" #include "env-inl.h" #include "node.h" +#include "node_internals.h" #include "node_snapshot_builder.h" using v8::Context; @@ -127,7 +128,7 @@ CommonEnvironmentSetup::CommonEnvironmentSetup( if (flags & Flags::kIsForSnapshotting) { // The isolate must be registered before the SnapshotCreator initializes the // isolate, so that the memory reducer can be initialized. - isolate = impl_->isolate = Isolate::Allocate(); + isolate = impl_->isolate = Isolate::Allocate(GetOrCreateIsolateGroup()); platform->RegisterIsolate(isolate, loop); impl_->snapshot_creator.emplace(isolate, params); diff --git a/src/api/environment.cc b/src/api/environment.cc index b806e0e4af70d0..8126749cc2034b 100644 --- a/src/api/environment.cc +++ b/src/api/environment.cc @@ -6,6 +6,7 @@ #include "node.h" #include "node_builtins.h" #include "node_context_data.h" +#include "node_debug.h" #include "node_errors.h" #include "node_exit_code.h" #include "node_internals.h" @@ -37,6 +38,7 @@ using v8::Function; using v8::FunctionCallbackInfo; using v8::HandleScope; using v8::Isolate; +using v8::IsolateGroup; using v8::Just; using v8::JustVoid; using v8::Local; @@ -111,10 +113,13 @@ MaybeLocal PrepareStackTraceCallback(Local context, void* NodeArrayBufferAllocator::Allocate(size_t size) { void* ret; - if (zero_fill_field_ || per_process::cli_options->zero_fill_all_buffers) + if (zero_fill_field_ || per_process::cli_options->zero_fill_all_buffers) { + COUNT_GENERIC_USAGE("NodeArrayBufferAllocator.Allocate.ZeroFilled"); ret = allocator_->Allocate(size); - else + } else { + COUNT_GENERIC_USAGE("NodeArrayBufferAllocator.Allocate.Uninitialized"); ret = allocator_->AllocateUninitialized(size); + } if (ret != nullptr) [[likely]] { total_mem_usage_.fetch_add(size, std::memory_order_relaxed); } @@ -122,6 +127,7 @@ void* NodeArrayBufferAllocator::Allocate(size_t size) { } void* NodeArrayBufferAllocator::AllocateUninitialized(size_t size) { + COUNT_GENERIC_USAGE("NodeArrayBufferAllocator.Allocate.Uninitialized"); void* ret = allocator_->AllocateUninitialized(size); if (ret != nullptr) [[likely]] { total_mem_usage_.fetch_add(size, std::memory_order_relaxed); @@ -304,6 +310,17 @@ void SetIsolateUpForNode(v8::Isolate* isolate) { SetIsolateUpForNode(isolate, settings); } +// +IsolateGroup GetOrCreateIsolateGroup() { + // When pointer compression is disabled, we cannot create new groups, + // in which case we'll always return the default. + if (IsolateGroup::CanCreateNewGroups()) { + return IsolateGroup::Create(); + } + + return IsolateGroup::GetDefault(); +} + // TODO(joyeecheung): we may want to expose this, but then we need to be // careful about what we override in the params. Isolate* NewIsolate(Isolate::CreateParams* params, @@ -311,7 +328,8 @@ Isolate* NewIsolate(Isolate::CreateParams* params, MultiIsolatePlatform* platform, const SnapshotData* snapshot_data, const IsolateSettings& settings) { - Isolate* isolate = Isolate::Allocate(); + IsolateGroup group = GetOrCreateIsolateGroup(); + Isolate* isolate = Isolate::Allocate(group); if (isolate == nullptr) return nullptr; if (snapshot_data != nullptr) { @@ -853,8 +871,7 @@ Maybe InitializePrimordials(Local context, if (!GetPerContextExports(context).ToLocal(&exports)) { return Nothing(); } - Local primordials_string = - FIXED_ONE_BYTE_STRING(isolate, "primordials"); + Local primordials_string = isolate_data->primordials_string(); // Ensure that `InitializePrimordials` is called exactly once on a given // context. CHECK(!exports->Has(context, primordials_string).FromJust()); diff --git a/src/api/hooks.cc b/src/api/hooks.cc index 7b73f7db2427d9..0594dca02cd60a 100644 --- a/src/api/hooks.cc +++ b/src/api/hooks.cc @@ -201,9 +201,18 @@ async_context EmitAsyncInit(Isolate* isolate, Local resource, const char* name, async_id trigger_async_id) { + return EmitAsyncInit( + isolate, resource, std::string_view(name), trigger_async_id); +} + +async_context EmitAsyncInit(Isolate* isolate, + Local resource, + std::string_view name, + async_id trigger_async_id) { HandleScope handle_scope(isolate); Local type = - String::NewFromUtf8(isolate, name, NewStringType::kInternalized) + String::NewFromUtf8( + isolate, name.data(), NewStringType::kInternalized, name.size()) .ToLocalChecked(); return EmitAsyncInit(isolate, resource, type, trigger_async_id); } diff --git a/src/env_properties.h b/src/env_properties.h index 5c5271e344da2b..114f23c9860dcf 100644 --- a/src/env_properties.h +++ b/src/env_properties.h @@ -130,6 +130,7 @@ V(cwd_string, "cwd") \ V(data_string, "data") \ V(default_is_true_string, "defaultIsTrue") \ + V(defensive_string, "defensive") \ V(deserialize_info_string, "deserializeInfo") \ V(dest_string, "dest") \ V(destroyed_string, "destroyed") \ diff --git a/src/inspector/network_agent.cc b/src/inspector/network_agent.cc index f8fcf7be2256fe..45b9cc2af591c6 100644 --- a/src/inspector/network_agent.cc +++ b/src/inspector/network_agent.cc @@ -26,6 +26,8 @@ using v8::Object; using v8::Uint8Array; using v8::Value; +constexpr size_t kDefaultMaxTotalBufferSize = 100 * 1024 * 1024; // 100MB + // Get a protocol string property from the object. Maybe ObjectGetProtocolString(v8::Local context, Local object, @@ -246,7 +248,8 @@ NetworkAgent::NetworkAgent( : inspector_(inspector), v8_inspector_(v8_inspector), env_(env), - network_resource_manager_(std::move(network_resource_manager)) { + network_resource_manager_(std::move(network_resource_manager)), + requests_(kDefaultMaxTotalBufferSize) { event_notifier_map_["requestWillBeSent"] = &NetworkAgent::requestWillBeSent; event_notifier_map_["responseReceived"] = &NetworkAgent::responseReceived; event_notifier_map_["loadingFailed"] = &NetworkAgent::loadingFailed; @@ -329,8 +332,15 @@ void NetworkAgent::Wire(protocol::UberDispatcher* dispatcher) { protocol::Network::Dispatcher::wire(dispatcher, this); } -protocol::DispatchResponse NetworkAgent::enable() { +protocol::DispatchResponse NetworkAgent::enable( + std::optional in_maxTotalBufferSize, + std::optional in_maxResourceBufferSize) { inspector_->Enable(); + requests_ = RequestsBuffer( + in_maxTotalBufferSize.value_or(kDefaultMaxTotalBufferSize)); + if (in_maxResourceBufferSize) { + max_resource_buffer_size_ = *in_maxResourceBufferSize; + } return protocol::DispatchResponse::Success(); } @@ -341,7 +351,7 @@ protocol::DispatchResponse NetworkAgent::disable() { protocol::DispatchResponse NetworkAgent::getRequestPostData( const protocol::String& in_requestId, protocol::String* out_postData) { - auto request_entry = requests_.find(in_requestId); + auto request_entry = requests_.cfind(in_requestId); if (request_entry == requests_.end()) { // Request not found, ignore it. return protocol::DispatchResponse::InvalidParams("Request not found"); @@ -362,7 +372,7 @@ protocol::DispatchResponse NetworkAgent::getRequestPostData( // Concat response bodies. protocol::Binary buf = - protocol::Binary::concat(request_entry->second.request_data_blobs); + protocol::Binary::concat(request_entry->second.request_data_blobs()); *out_postData = protocol::StringUtil::fromUTF8(buf.data(), buf.size()); return protocol::DispatchResponse::Success(); } @@ -371,7 +381,7 @@ protocol::DispatchResponse NetworkAgent::getResponseBody( const protocol::String& in_requestId, protocol::String* out_body, bool* out_base64Encoded) { - auto request_entry = requests_.find(in_requestId); + auto request_entry = requests_.cfind(in_requestId); if (request_entry == requests_.end()) { // Request not found, ignore it. return protocol::DispatchResponse::InvalidParams("Request not found"); @@ -391,7 +401,7 @@ protocol::DispatchResponse NetworkAgent::getResponseBody( // Concat response bodies. protocol::Binary buf = - protocol::Binary::concat(request_entry->second.response_data_blobs); + protocol::Binary::concat(request_entry->second.response_data_blobs()); if (request_entry->second.response_charset == Charset::kBinary) { // If the response is binary, we return base64 encoded data. *out_body = buf.toBase64(); @@ -410,22 +420,26 @@ protocol::DispatchResponse NetworkAgent::getResponseBody( protocol::DispatchResponse NetworkAgent::streamResourceContent( const protocol::String& in_requestId, protocol::Binary* out_bufferedData) { - auto it = requests_.find(in_requestId); - if (it == requests_.end()) { - // Request not found, ignore it. - return protocol::DispatchResponse::InvalidParams("Request not found"); - } - auto& request_entry = it->second; + bool is_response_finished = false; + { + auto it = requests_.find(in_requestId); + if (it == requests_.end()) { + // Request not found, ignore it. + return protocol::DispatchResponse::InvalidParams("Request not found"); + } + auto& request_entry = it->second; - request_entry.is_streaming = true; + request_entry.is_streaming = true; - // Concat response bodies. - *out_bufferedData = - protocol::Binary::concat(request_entry.response_data_blobs); - // Clear buffered data. - request_entry.response_data_blobs.clear(); + // Concat response bodies. + *out_bufferedData = + protocol::Binary::concat(request_entry.response_data_blobs()); + // Clear buffered data. + request_entry.clear_response_data_blobs(); + is_response_finished = request_entry.is_response_finished; + } - if (request_entry.is_response_finished) { + if (is_response_finished) { // If the request is finished, remove the entry. requests_.erase(in_requestId); } @@ -500,9 +514,11 @@ void NetworkAgent::requestWillBeSent(v8::Local context, } auto request_charset = charset == "utf-8" ? Charset::kUTF8 : Charset::kBinary; - requests_.emplace( - request_id, - RequestEntry(timestamp, request_charset, request->getHasPostData())); + requests_.emplace(request_id, + RequestEntry(timestamp, + request_charset, + request->getHasPostData(), + max_resource_buffer_size_)); frontend_->requestWillBeSent(request_id, std::move(request), std::move(initiator), @@ -580,7 +596,7 @@ void NetworkAgent::loadingFinished(v8::Local context, frontend_->loadingFinished(request_id, timestamp); - auto request_entry = requests_.find(request_id); + auto request_entry = requests_.cfind(request_id); if (request_entry == requests_.end()) { // No entry found. Ignore it. return; @@ -590,7 +606,7 @@ void NetworkAgent::loadingFinished(v8::Local context, // Streaming finished, remove the entry. requests_.erase(request_id); } else { - request_entry->second.is_response_finished = true; + requests_.find(request_id)->second.is_response_finished = true; } } @@ -631,7 +647,7 @@ void NetworkAgent::dataSent(v8::Local context, } Local data = data_obj.As(); auto data_bin = protocol::Binary::fromUint8Array(data); - request_entry->second.request_data_blobs.push_back(data_bin); + request_entry->second.push_request_data_blob(data_bin); } void NetworkAgent::dataReceived(v8::Local context, @@ -673,7 +689,7 @@ void NetworkAgent::dataReceived(v8::Local context, frontend_->dataReceived( request_id, timestamp, data_length, encoded_data_length, data_bin); } else { - request_entry.response_data_blobs.push_back(data_bin); + request_entry.push_response_data_blob(data_bin); } } diff --git a/src/inspector/network_agent.h b/src/inspector/network_agent.h index 487c7d02680d3d..7a5d545cb8d499 100644 --- a/src/inspector/network_agent.h +++ b/src/inspector/network_agent.h @@ -3,6 +3,7 @@ #include "env.h" #include "io_agent.h" +#include "network_requests_buffer.h" #include "network_resource_manager.h" #include "node/inspector/protocol/Network.h" @@ -15,31 +16,6 @@ namespace inspector { class NetworkInspector; -// Supported charsets for devtools frontend on request/response data. -// If the charset is kUTF8, the data is expected to be text and can be -// formatted on the frontend. -enum class Charset { - kUTF8, - kBinary, -}; - -struct RequestEntry { - double timestamp; - bool is_request_finished = false; - bool is_response_finished = false; - bool is_streaming = false; - Charset request_charset; - std::vector request_data_blobs; - Charset response_charset; - std::vector response_data_blobs; - - RequestEntry(double timestamp, Charset request_charset, bool has_request_body) - : timestamp(timestamp), - is_request_finished(!has_request_body), - request_charset(request_charset), - response_charset(Charset::kBinary) {} -}; - class NetworkAgent : public protocol::Network::Backend { public: explicit NetworkAgent( @@ -50,7 +26,9 @@ class NetworkAgent : public protocol::Network::Backend { void Wire(protocol::UberDispatcher* dispatcher); - protocol::DispatchResponse enable() override; + protocol::DispatchResponse enable( + std::optional in_maxTotalBufferSize, + std::optional in_maxResourceBufferSize) override; protocol::DispatchResponse disable() override; @@ -104,12 +82,17 @@ class NetworkAgent : public protocol::Network::Backend { NetworkInspector* inspector_; v8_inspector::V8Inspector* v8_inspector_; std::shared_ptr frontend_; + using EventNotifier = void (NetworkAgent::*)(v8::Local context, v8::Local); std::unordered_map event_notifier_map_; - std::map requests_; Environment* env_; std::shared_ptr network_resource_manager_; + + // Per-resource buffer size in bytes to use when preserving network payloads + // (XHRs, etc). + size_t max_resource_buffer_size_ = 5 * 1024 * 1024; // 5MB + RequestsBuffer requests_; }; } // namespace inspector diff --git a/src/inspector/network_requests_buffer.h b/src/inspector/network_requests_buffer.h new file mode 100644 index 00000000000000..9f759a0c19381f --- /dev/null +++ b/src/inspector/network_requests_buffer.h @@ -0,0 +1,195 @@ +#ifndef SRC_INSPECTOR_NETWORK_REQUESTS_BUFFER_H_ +#define SRC_INSPECTOR_NETWORK_REQUESTS_BUFFER_H_ + +#include +#include +#include + +#include "inspector/node_string.h" + +namespace node { +namespace inspector { + +// Supported charsets for devtools frontend on request/response data. +// If the charset is kUTF8, the data is expected to be text and can be +// formatted on the frontend. +enum class Charset { + kUTF8, + kBinary, +}; + +struct RequestEntry { + double timestamp; + bool is_request_finished = false; + bool is_response_finished = false; + bool is_streaming = false; + Charset request_charset; + Charset response_charset = Charset::kBinary; + + size_t max_resource_buffer_size; + size_t buffer_size() const { + return request_data_size_ + response_data_size_; + } + + const std::vector request_data_blobs() const { + return request_data_blobs_; + } + const std::vector response_data_blobs() const { + return response_data_blobs_; + } + + void push_request_data_blob(const protocol::Binary& blob) { + if (buffer_size() + blob.size() > max_resource_buffer_size) { + // Exceed the per-resource buffer size limit, ignore the blob. + return; + } + request_data_blobs_.push_back(blob); + request_data_size_ += blob.size(); + } + void push_response_data_blob(const protocol::Binary& blob) { + if (buffer_size() + blob.size() > max_resource_buffer_size) { + // Exceed the per-resource buffer size limit, ignore the blob. + return; + } + response_data_blobs_.push_back(blob); + response_data_size_ += blob.size(); + } + + void clear_response_data_blobs() { + response_data_blobs_.clear(); + response_data_size_ = 0; + } + + RequestEntry(double timestamp, + Charset request_charset, + bool has_request_body, + size_t max_resource_buffer_size) + : timestamp(timestamp), + is_request_finished(!has_request_body), + request_charset(request_charset), + max_resource_buffer_size(max_resource_buffer_size) {} + + private: + size_t request_data_size_ = 0; + std::vector request_data_blobs_; + size_t response_data_size_ = 0; + std::vector response_data_blobs_; +}; + +class RequestsBuffer { + using key_value_pair_t = typename std::pair; + using list_iterator = typename std::list::iterator; + using const_list_iterator = + typename std::list::const_iterator; + + public: + struct mutable_iterator { + list_iterator it; + size_t buffer_size; + RequestsBuffer* parent; + + key_value_pair_t& operator*() { return *it; } + key_value_pair_t* operator->() { return &(*it); } + bool operator==(const const_list_iterator& other) const { + return it == other; + } + bool operator!=(const const_list_iterator& other) const { + return it != other; + } + + mutable_iterator(std::nullptr_t, RequestsBuffer* parent) + : it(parent->list_.end()), buffer_size(0), parent(parent) {} + + mutable_iterator(list_iterator it, RequestsBuffer* parent) + : it(it), buffer_size(it->second.buffer_size()), parent(parent) {} + ~mutable_iterator() { + if (it == parent->list_.end()) return; + // The entry must not have been erased. + DCHECK(parent->lookup_map_.contains(it->first)); + // Update the total buffer size if the buffer size has changed. + if (buffer_size == it->second.buffer_size()) { + return; + } + parent->total_buffer_size_ += it->second.buffer_size(); + DCHECK_GE(parent->total_buffer_size_, buffer_size); + parent->total_buffer_size_ -= buffer_size; + parent->enforceBufferLimits(); + } + }; + + explicit RequestsBuffer(size_t max_total_buffer_size) + : max_total_buffer_size_(max_total_buffer_size) {} + + size_t max_total_buffer_size() const { return max_total_buffer_size_; } + size_t total_buffer_size() const { return total_buffer_size_; } + + const_list_iterator begin() const { return list_.begin(); } + const_list_iterator end() const { return list_.end(); } + + // Get a mutable iterator to the entry with the given key. + // The iterator will update the total buffer size when it is destroyed. + mutable_iterator find(const protocol::String& key) { + auto it = lookup_map_.find(key); + if (it == lookup_map_.end()) { + return mutable_iterator(nullptr, this); + } + return mutable_iterator(it->second, this); + } + const_list_iterator cfind(const protocol::String& key) const { + auto it = lookup_map_.find(key); + if (it == lookup_map_.end()) { + return list_.end(); + } + return it->second; + } + + bool contains(const protocol::String& key) const { + return lookup_map_.contains(key); + } + + bool emplace(const protocol::String& key, RequestEntry&& value) { + if (lookup_map_.contains(key)) { + return false; + } + list_.emplace_front(key, std::move(value)); + lookup_map_[key] = list_.begin(); + // No buffer yet. + DCHECK_EQ(begin()->second.buffer_size(), 0u); + return true; + } + + void erase(const protocol::String& key) { + auto it = lookup_map_.find(key); + if (it == lookup_map_.end()) { + return; + } + erase(it->second); + } + + void erase(const_list_iterator it) { + DCHECK(it != list_.end()); + DCHECK_GE(total_buffer_size_, it->second.buffer_size()); + total_buffer_size_ -= it->second.buffer_size(); + lookup_map_.erase(it->first); + list_.erase(it); + } + + private: + void enforceBufferLimits() { + while (total_buffer_size_ > max_total_buffer_size_ && !list_.empty()) { + auto last_it = --list_.end(); + erase(last_it); + } + } + + size_t max_total_buffer_size_; + size_t total_buffer_size_ = 0; + + std::list list_; + std::map lookup_map_; +}; + +} // namespace inspector +} // namespace node + +#endif // SRC_INSPECTOR_NETWORK_REQUESTS_BUFFER_H_ diff --git a/src/inspector/node_protocol.pdl b/src/inspector/node_protocol.pdl index a79dd3595ce493..24cff73585aeb7 100644 --- a/src/inspector/node_protocol.pdl +++ b/src/inspector/node_protocol.pdl @@ -200,6 +200,11 @@ experimental domain Network # Enables network tracking, network events will now be delivered to the client. command enable + parameters + # Buffer size in bytes to use when preserving network payloads (XHRs, etc). + experimental optional integer maxTotalBufferSize + # Per-resource buffer size in bytes to use when preserving network payloads (XHRs, etc). + experimental optional integer maxResourceBufferSize # Returns post data sent with the request. Returns an error when no data was sent with the request. command getRequestPostData diff --git a/src/inspector_js_api.cc b/src/inspector_js_api.cc index cbad4651f1db21..54a91765ac6b1f 100644 --- a/src/inspector_js_api.cc +++ b/src/inspector_js_api.cc @@ -240,8 +240,8 @@ static void AsyncTaskScheduledWrapper(const FunctionCallbackInfo& args) { CHECK(args[0]->IsString()); Local task_name = args[0].As(); - String::Value task_name_value(args.GetIsolate(), task_name); - StringView task_name_view(*task_name_value, task_name_value.length()); + TwoByteValue task_name_value(args.GetIsolate(), task_name); + StringView task_name_view(task_name_value.out(), task_name_value.length()); CHECK(args[1]->IsNumber()); int64_t task_id; diff --git a/src/module_wrap.cc b/src/module_wrap.cc index 729b71e905d75e..8870b643e0be55 100644 --- a/src/module_wrap.cc +++ b/src/module_wrap.cc @@ -165,15 +165,6 @@ ModuleWrap::ModuleWrap(Realm* realm, } MakeWeak(); module_.SetWeak(); - - HandleScope scope(realm->isolate()); - Local context = realm->context(); - Local requests = module->GetModuleRequests(); - for (int i = 0; i < requests->Length(); i++) { - ModuleCacheKey module_cache_key = ModuleCacheKey::From( - context, requests->Get(context, i).As()); - resolve_cache_[module_cache_key] = i; - } } ModuleWrap::~ModuleWrap() { @@ -194,30 +185,6 @@ Local ModuleWrap::context() const { return obj.As()->GetCreationContextChecked(); } -ModuleWrap* ModuleWrap::GetLinkedRequest(uint32_t index) { - DCHECK(IsLinked()); - Isolate* isolate = env()->isolate(); - EscapableHandleScope scope(isolate); - Local linked_requests_data = - object()->GetInternalField(kLinkedRequestsSlot); - DCHECK(linked_requests_data->IsValue() && - linked_requests_data.As()->IsArray()); - Local requests = linked_requests_data.As(); - - CHECK_LT(index, requests->Length()); - - Local module_value; - if (!requests->Get(context(), index).ToLocal(&module_value)) { - return nullptr; - } - CHECK(module_value->IsObject()); - Local module_object = module_value.As(); - - ModuleWrap* module_wrap; - ASSIGN_OR_RETURN_UNWRAP(&module_wrap, module_object, nullptr); - return module_wrap; -} - ModuleWrap* ModuleWrap::GetFromModule(Environment* env, Local module) { auto range = env->hash_to_module_map.equal_range(module->GetIdentityHash()); @@ -563,8 +530,9 @@ ModulePhase to_phase_constant(ModuleImportPhase phase) { return kEvaluationPhase; case ModuleImportPhase::kSource: return kSourcePhase; + default: + UNREACHABLE(); } - UNREACHABLE(); } static Local createImportAttributesContainer( @@ -653,6 +621,7 @@ void ModuleWrap::GetModuleRequests(const FunctionCallbackInfo& args) { // moduleWrap.link(moduleWraps) void ModuleWrap::Link(const FunctionCallbackInfo& args) { Isolate* isolate = args.GetIsolate(); + HandleScope handle_scope(isolate); Realm* realm = Realm::GetCurrent(args); Local context = realm->context(); @@ -664,33 +633,70 @@ void ModuleWrap::Link(const FunctionCallbackInfo& args) { Local requests = dependent->module_.Get(isolate)->GetModuleRequests(); Local modules = args[0].As(); - CHECK_EQ(modules->Length(), static_cast(requests->Length())); - - for (int i = 0; i < requests->Length(); i++) { + std::vector> modules_vector; + if (FromV8Array(context, modules, &modules_vector).IsEmpty()) { + return; + } + size_t request_count = static_cast(requests->Length()); + CHECK_EQ(modules_vector.size(), request_count); + std::vector linked_module_wraps(request_count); + + // Track the duplicated module requests. For example if a modulelooks like + // this: + // + // import { foo } from 'mod' with { type: 'json' }; + // import source ModSource from 'mod' with { type: 'json' }; + // import { baz } from 'mod2'; + // + // The first two module requests are identical. The map would look like + // { mod_key: 0, mod2_key: 2 } in this case, so that module request 0 and + // module request 1 would be mapped to mod_key and both should resolve to the + // module identified by module request 0 (the first one with this identity), + // and module request 2 should resolve the module identified by index 2. + std::unordered_map + module_request_map; + + for (size_t i = 0; i < request_count; i++) { + // TODO(joyeecheung): merge this with the serializeKey() in module_map.js. + // This currently doesn't sort the import attributes. + Local module_value = modules_vector[i].Get(isolate); ModuleCacheKey module_cache_key = ModuleCacheKey::From( context, requests->Get(context, i).As()); - DCHECK(dependent->resolve_cache_.contains(module_cache_key)); - - Local module_i; - Local module_cache_i; - uint32_t coalesced_index = dependent->resolve_cache_[module_cache_key]; - if (!modules->Get(context, i).ToLocal(&module_i) || - !modules->Get(context, coalesced_index).ToLocal(&module_cache_i) || - !module_i->StrictEquals(module_cache_i)) { - // If the module is different from the one of the same request, throw an - // error. - THROW_ERR_MODULE_LINK_MISMATCH( - realm->env(), - "Module request '%s' at index %d must be linked " - "to the same module requested at index %d", - module_cache_key.ToString(), - i, - coalesced_index); - return; + auto it = module_request_map.find(module_cache_key); + if (it == module_request_map.end()) { + // This is the first request with this identity, record it - any mismatch + // for this would only be found in subsequent requests, so no need to + // check here. + module_request_map[module_cache_key] = i; + } else { // This identity has been seen before, check for mismatch. + size_t first_seen_index = it->second; + // Check that the module is the same as the one resolved by the first + // request with this identity. + Local first_seen_value = + modules_vector[first_seen_index].Get(isolate); + if (!module_value->StrictEquals(first_seen_value)) { + // If the module is different from the one of the same request, throw an + // error. + THROW_ERR_MODULE_LINK_MISMATCH( + realm->env(), + "Module request '%s' at index %d must be linked " + "to the same module requested at index %d", + module_cache_key.ToString(), + i, + first_seen_index); + return; + } } + + CHECK(module_value->IsObject()); // Guaranteed by link methods in JS land. + ModuleWrap* resolved = + BaseObject::Unwrap(module_value.As()); + CHECK_NOT_NULL(resolved); // Guaranteed by link methods in JS land. + linked_module_wraps[i] = resolved; } args.This()->SetInternalField(kLinkedRequestsSlot, modules); + std::swap(dependent->linked_module_wraps_, linked_module_wraps); dependent->linked_ = true; } @@ -1012,11 +1018,10 @@ void ModuleWrap::HasAsyncGraph(Local property, // static MaybeLocal ModuleWrap::ResolveModuleCallback( Local context, - Local specifier, - Local import_attributes, + size_t module_request_index, Local referrer) { ModuleWrap* resolved_module; - if (!ResolveModule(context, specifier, import_attributes, referrer) + if (!ResolveModule(context, module_request_index, referrer) .To(&resolved_module)) { return {}; } @@ -1027,11 +1032,10 @@ MaybeLocal ModuleWrap::ResolveModuleCallback( // static MaybeLocal ModuleWrap::ResolveSourceCallback( Local context, - Local specifier, - Local import_attributes, + size_t module_request_index, Local referrer) { ModuleWrap* resolved_module; - if (!ResolveModule(context, specifier, import_attributes, referrer) + if (!ResolveModule(context, module_request_index, referrer) .To(&resolved_module)) { return {}; } @@ -1050,12 +1054,22 @@ MaybeLocal ModuleWrap::ResolveSourceCallback( return module_source_object.As(); } +static std::string GetSpecifierFromModuleRequest(Local context, + Local referrer, + size_t module_request_index) { + Local raw_request = + referrer->GetModuleRequests() + ->Get(context, static_cast(module_request_index)) + .As(); + Local specifier = raw_request->GetSpecifier(); + Utf8Value specifier_utf8(Isolate::GetCurrent(), specifier); + return specifier_utf8.ToString(); +} + // static -Maybe ModuleWrap::ResolveModule( - Local context, - Local specifier, - Local import_attributes, - Local referrer) { +Maybe ModuleWrap::ResolveModule(Local context, + size_t module_request_index, + Local referrer) { Isolate* isolate = Isolate::GetCurrent(); Environment* env = Environment::GetCurrent(context); if (env == nullptr) { @@ -1065,37 +1079,34 @@ Maybe ModuleWrap::ResolveModule( // Check that the referrer is not yet been instantiated. DCHECK(referrer->GetStatus() <= Module::kInstantiated); - ModuleCacheKey cache_key = - ModuleCacheKey::From(context, specifier, import_attributes); - ModuleWrap* dependent = ModuleWrap::GetFromModule(env, referrer); if (dependent == nullptr) { + std::string specifier = + GetSpecifierFromModuleRequest(context, referrer, module_request_index); THROW_ERR_VM_MODULE_LINK_FAILURE( - env, "request for '%s' is from invalid module", cache_key.specifier); + env, "request for '%s' is from invalid module", specifier); return Nothing(); } if (!dependent->IsLinked()) { + std::string specifier = + GetSpecifierFromModuleRequest(context, referrer, module_request_index); THROW_ERR_VM_MODULE_LINK_FAILURE(env, "request for '%s' can not be resolved on " "module '%s' that is not linked", - cache_key.specifier, + specifier, dependent->url_); return Nothing(); } - auto it = dependent->resolve_cache_.find(cache_key); - if (it == dependent->resolve_cache_.end()) { - THROW_ERR_VM_MODULE_LINK_FAILURE( - env, - "request for '%s' is not cached on module '%s'", - cache_key.specifier, - dependent->url_); - return Nothing(); + size_t linked_module_count = dependent->linked_module_wraps_.size(); + if (linked_module_count > 0) { + CHECK_LT(module_request_index, linked_module_count); + } else { + UNREACHABLE("Module resolution callback invoked for a module" + " without linked requests"); } - ModuleWrap* module_wrap = dependent->GetLinkedRequest(it->second); - CHECK_NOT_NULL(module_wrap); - return Just(module_wrap); + return Just(dependent->linked_module_wraps_[module_request_index]); } static MaybeLocal ImportModuleDynamicallyWithPhase( diff --git a/src/module_wrap.h b/src/module_wrap.h index d81facabf8d80e..50703dc0269a9f 100644 --- a/src/module_wrap.h +++ b/src/module_wrap.h @@ -93,16 +93,14 @@ struct ModuleCacheKey : public MemoryRetainer { }; class ModuleWrap : public BaseObject { - using ResolveCache = - std::unordered_map; - public: enum InternalFields { kModuleSlot = BaseObject::kInternalFieldCount, kModuleSourceObjectSlot, kSyntheticEvaluationStepsSlot, kContextObjectSlot, // Object whose creation context is the target Context - kLinkedRequestsSlot, // Array of linked requests + kLinkedRequestsSlot, // Array of linked requests, each is a ModuleWrap JS + // wrapper object. kInternalFieldCount }; @@ -134,8 +132,6 @@ class ModuleWrap : public BaseObject { bool IsLinked() const { return linked_; } - ModuleWrap* GetLinkedRequest(uint32_t index); - static v8::Local GetHostDefinedOptions( v8::Isolate* isolate, v8::Local symbol); @@ -196,27 +192,22 @@ class ModuleWrap : public BaseObject { static v8::MaybeLocal ResolveModuleCallback( v8::Local context, - v8::Local specifier, - v8::Local import_attributes, + size_t module_request_index, v8::Local referrer); static v8::MaybeLocal ResolveSourceCallback( v8::Local context, - v8::Local specifier, - v8::Local import_attributes, + size_t module_request_index, v8::Local referrer); static ModuleWrap* GetFromModule(node::Environment*, v8::Local); // This method may throw a JavaScript exception, so the return type is // wrapped in a Maybe. - static v8::Maybe ResolveModule( - v8::Local context, - v8::Local specifier, - v8::Local import_attributes, - v8::Local referrer); + static v8::Maybe ResolveModule(v8::Local context, + size_t module_request_index, + v8::Local referrer); std::string url_; v8::Global module_; - ResolveCache resolve_cache_; contextify::ContextifyContext* contextify_context_ = nullptr; bool synthetic_ = false; bool linked_ = false; @@ -224,6 +215,11 @@ class ModuleWrap : public BaseObject { // nullopt value. std::optional has_async_graph_ = std::nullopt; int module_hash_; + // Corresponds to the ModuleWrap* of the wrappers in kLinkedRequestsSlot. + // These are populated during Link(), and are only valid after that as + // convenient shortcuts, but do not hold the ModuleWraps alive. The actual + // strong references come from the array in kLinkedRequestsSlot. + std::vector linked_module_wraps_; }; } // namespace loader diff --git a/src/node.cc b/src/node.cc index 72d6973723a637..a18660d388be9d 100644 --- a/src/node.cc +++ b/src/node.cc @@ -780,7 +780,10 @@ static ExitCode ProcessGlobalArgsInternal(std::vector* args, env_opts->abort_on_uncaught_exception = true; } - v8_args.emplace_back("--js-source-phase-imports"); + if (std::ranges::find(v8_args, "--no-js-source-phase-imports") == + v8_args.end()) { + v8_args.emplace_back("--js-source-phase-imports"); + } #ifdef __POSIX__ // Block SIGPROF signals when sleeping in epoll_wait/kevent/etc. Avoids the diff --git a/src/node.h b/src/node.h index 94c307e3f84923..70b60e79ac2969 100644 --- a/src/node.h +++ b/src/node.h @@ -1402,6 +1402,10 @@ NODE_EXTERN async_context EmitAsyncInit(v8::Isolate* isolate, v8::Local resource, const char* name, async_id trigger_async_id = -1); +NODE_EXTERN async_context EmitAsyncInit(v8::Isolate* isolate, + v8::Local resource, + std::string_view name, + async_id trigger_async_id = -1); NODE_EXTERN async_context EmitAsyncInit(v8::Isolate* isolate, v8::Local resource, @@ -1508,6 +1512,10 @@ class NODE_EXTERN AsyncResource { v8::Local resource, const char* name, async_id trigger_async_id = -1); + AsyncResource(v8::Isolate* isolate, + v8::Local resource, + std::string_view name, + async_id trigger_async_id = -1); virtual ~AsyncResource(); diff --git a/src/node_api.cc b/src/node_api.cc index 7274030f9491ef..a72ba0ddc96816 100644 --- a/src/node_api.cc +++ b/src/node_api.cc @@ -212,7 +212,7 @@ class ThreadSafeFunction : public node::AsyncResource { napi_threadsafe_function_call_js call_js_cb_) : AsyncResource(env_->isolate, resource, - *v8::String::Utf8Value(env_->isolate, name)), + node::Utf8Value(env_->isolate, name).ToStringView()), thread_count(thread_count_), is_closing(false), dispatch_state(kDispatchIdle), @@ -1150,7 +1150,7 @@ class Work : public node::AsyncResource, public node::ThreadPoolWork { : AsyncResource( env->isolate, async_resource, - *v8::String::Utf8Value(env->isolate, async_resource_name)), + node::Utf8Value(env->isolate, async_resource_name).ToStringView()), ThreadPoolWork(env->node_env(), "node_api"), _env(env), _data(data), diff --git a/src/node_buffer.cc b/src/node_buffer.cc index 0e4d437c1ea501..cc418017f76803 100644 --- a/src/node_buffer.cc +++ b/src/node_buffer.cc @@ -986,11 +986,7 @@ void IndexOfString(const FunctionCallbackInfo& args) { size_t result = haystack_length; if (enc == UCS2) { - String::Value needle_value(isolate, needle); - if (*needle_value == nullptr) { - return args.GetReturnValue().Set(-1); - } - + TwoByteValue needle_value(isolate, needle); if (haystack_length < 2 || needle_value.length() < 1) { return args.GetReturnValue().Set(-1); } @@ -1011,27 +1007,27 @@ void IndexOfString(const FunctionCallbackInfo& args) { offset / 2, is_forward); } else { - result = - nbytes::SearchString(reinterpret_cast(haystack), - haystack_length / 2, - reinterpret_cast(*needle_value), - needle_value.length(), - offset / 2, - is_forward); + result = nbytes::SearchString(reinterpret_cast(haystack), + haystack_length / 2, + needle_value.out(), + needle_value.length(), + offset / 2, + is_forward); } result *= 2; } else if (enc == UTF8) { - String::Utf8Value needle_value(isolate, needle); + Utf8Value needle_value(isolate, needle); if (*needle_value == nullptr) return args.GetReturnValue().Set(-1); - - result = - nbytes::SearchString(reinterpret_cast(haystack), - haystack_length, - reinterpret_cast(*needle_value), - needle_length, - offset, - is_forward); + CHECK_GE(needle_length, needle_value.length()); + + result = nbytes::SearchString( + reinterpret_cast(haystack), + haystack_length, + reinterpret_cast(needle_value.out()), + needle_length, + offset, + is_forward); } else if (enc == LATIN1) { uint8_t* needle_data = node::UncheckedMalloc(needle_length); if (needle_data == nullptr) { @@ -1316,10 +1312,10 @@ static void Btoa(const FunctionCallbackInfo& args) { input->Length(), buffer.out()); } else { - String::Value value(env->isolate(), input); + TwoByteValue value(env->isolate(), input); MaybeStackBuffer stack_buf(value.length()); size_t out_len = simdutf::convert_utf16_to_latin1( - reinterpret_cast(*value), + reinterpret_cast(value.out()), value.length(), stack_buf.out()); if (out_len == 0) { // error @@ -1370,8 +1366,8 @@ static void Atob(const FunctionCallbackInfo& args) { buffer.SetLength(expected_length); result = simdutf::base64_to_binary(data, input->Length(), buffer.out()); } else { // 16-bit case - String::Value value(env->isolate(), input); - auto data = reinterpret_cast(*value); + TwoByteValue value(env->isolate(), input); + auto data = reinterpret_cast(value.out()); size_t expected_length = simdutf::maximal_binary_length_from_base64(data, value.length()); buffer.AllocateSufficientStorage(expected_length); diff --git a/src/node_debug.cc b/src/node_debug.cc index bb0e11747e418c..8dbc79004ff90a 100644 --- a/src/node_debug.cc +++ b/src/node_debug.cc @@ -8,6 +8,7 @@ #include "v8-fast-api-calls.h" #include "v8.h" +#include #include #include #endif // DEBUG @@ -23,9 +24,20 @@ using v8::Number; using v8::Object; using v8::Value; +thread_local std::unordered_map generic_usage_counters; thread_local std::unordered_map v8_fast_api_call_counts; +void CountGenericUsage(const char* counter_name) { + if (generic_usage_counters.find(counter_name) == generic_usage_counters.end()) + generic_usage_counters[counter_name] = 0; + generic_usage_counters[counter_name]++; +} + +int GetGenericUsageCount(const char* counter_name) { + return generic_usage_counters[counter_name]; +} + void TrackV8FastApiCall(FastStringKey key) { v8_fast_api_call_counts[key]++; } @@ -34,6 +46,17 @@ int GetV8FastApiCallCount(FastStringKey key) { return v8_fast_api_call_counts[key]; } +void GetGenericUsageCount(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + if (!args[0]->IsString()) { + env->ThrowError("getGenericUsageCount must be called with a string"); + return; + } + Utf8Value utf8_key(env->isolate(), args[0]); + args.GetReturnValue().Set( + GetGenericUsageCount(utf8_key.ToStringView().data())); +} + void GetV8FastApiCallCount(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); if (!args[0]->IsString()) { @@ -89,6 +112,7 @@ void Initialize(Local target, Local context, void* priv) { SetMethod(context, target, "getV8FastApiCallCount", GetV8FastApiCallCount); + SetMethod(context, target, "getGenericUsageCount", GetGenericUsageCount); SetFastMethod(context, target, "isEven", SlowIsEven, &fast_is_even); SetFastMethod(context, target, "isOdd", SlowIsOdd, &fast_is_odd); } diff --git a/src/node_debug.h b/src/node_debug.h index 32fbf21c813366..6d9e03a6723dd0 100644 --- a/src/node_debug.h +++ b/src/node_debug.h @@ -13,10 +13,14 @@ namespace debug { void TrackV8FastApiCall(FastStringKey key); int GetV8FastApiCallCount(FastStringKey key); +void CountGenericUsage(const char* counter_name); +#define COUNT_GENERIC_USAGE(name) node::debug::CountGenericUsage(name) + #define TRACK_V8_FAST_API_CALL(key) \ node::debug::TrackV8FastApiCall(FastStringKey(key)) #else // !DEBUG #define TRACK_V8_FAST_API_CALL(key) +#define COUNT_GENERIC_USAGE(name) #endif // DEBUG } // namespace debug diff --git a/src/node_errors.cc b/src/node_errors.cc index 7ad099a11b98aa..ec84aaafbdc99a 100644 --- a/src/node_errors.cc +++ b/src/node_errors.cc @@ -1024,15 +1024,14 @@ void PerIsolateMessageListener(Local message, Local error) { break; } Utf8Value filename(isolate, message->GetScriptOrigin().ResourceName()); + Utf8Value msg(isolate, message->Get()); // (filename):(line) (message) - std::stringstream warning; - warning << *filename; - warning << ":"; - warning << message->GetLineNumber(env->context()).FromMaybe(-1); - warning << " "; - v8::String::Utf8Value msg(isolate, message->Get()); - warning << *msg; - USE(ProcessEmitWarningGeneric(env, warning.str().c_str(), "V8")); + std::string warning = + SPrintF("%s:%s %s", + filename, + message->GetLineNumber(env->context()).FromMaybe(-1), + msg); + USE(ProcessEmitWarningGeneric(env, warning, "V8")); break; } case Isolate::MessageErrorLevel::kMessageError: diff --git a/src/node_internals.h b/src/node_internals.h index 978994ee1c9c3a..6ec17b35cae6c6 100644 --- a/src/node_internals.h +++ b/src/node_internals.h @@ -56,6 +56,9 @@ extern uint64_t node_start_time; // Forward declaration class Environment; +static constexpr uint64_t kMaxPointerCompressionHeap = uint64_t{1} + << 32; // 4 GiB + // Convert a struct sockaddr to a { address: '1.2.3.4', port: 1234 } JS object. // Sets address and port properties on the info object and returns it. // If |info| is omitted, a new object is returned. @@ -342,6 +345,20 @@ void TraceEnvVar(Environment* env, v8::Local key); void DefineZlibConstants(v8::Local target); + +// If creating new v8::IsolateGroup instance is supported, this returns a +// new instance. Otherwise, it returns the default instance. +// +// An IsolateGroup is a collection of Isolates that share the same underlying +// pointer cage when pointer compression is enabled. When pointer compression is +// disabled, there is a default IsolateGroup that is used for all isolates, and +// when pointer compression is enabled, all isolates in the app share the +// same pointer cage by default that is limited a maximum of 4GB, not counting +// array buffers and off-heap storage. Multiple IsolateGroups can be used to +// work around the 4GB limit, but each group reserves a range of virtual memory +// addresses, so this should be used with care. +v8::IsolateGroup GetOrCreateIsolateGroup(); + v8::Isolate* NewIsolate(v8::Isolate::CreateParams* params, uv_loop_t* event_loop, MultiIsolatePlatform* platform, diff --git a/src/node_options.cc b/src/node_options.cc index 2d9ccba7fe4feb..687cbd5398535f 100644 --- a/src/node_options.cc +++ b/src/node_options.cc @@ -128,7 +128,15 @@ void PerIsolateOptions::HandleMaxOldSpaceSizePercentage( } // Get available memory in bytes +#ifdef V8_COMPRESS_POINTERS + // When pointer compression is enabled, V8 uses a 4 GiB heap limit. + // We'll use the smaller of that or the total system memory as + // reported by uv. + uint64_t total_memory = + std::min(uv_get_total_memory(), kMaxPointerCompressionHeap); // 4 GiB +#else uint64_t total_memory = uv_get_total_memory(); +#endif uint64_t constrained_memory = uv_get_constrained_memory(); // Use constrained memory if available, otherwise use total memory @@ -242,7 +250,7 @@ void EnvironmentOptions::CheckOptions(std::vector* errors, } else if (test_runner_force_exit) { errors->push_back("either --watch or --test-force-exit " "can be used, not both"); - } else if (!test_runner && (argv->size() < 1 || (*argv)[1].empty())) { + } else if (!test_runner && watch_mode_paths.empty() && argv->size() < 1) { errors->push_back("--watch requires specifying a file"); } @@ -1013,20 +1021,26 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() { AddOption("--watch", "run in watch mode", &EnvironmentOptions::watch_mode, - kAllowedInEnvvar); + kAllowedInEnvvar, + false, + OptionNamespaces::kWatchNamespace); AddOption("--watch-path", "path to watch", &EnvironmentOptions::watch_mode_paths, - kAllowedInEnvvar); + kAllowedInEnvvar, + OptionNamespaces::kWatchNamespace); AddOption("--watch-kill-signal", "kill signal to send to the process on watch mode restarts" "(default: SIGTERM)", &EnvironmentOptions::watch_mode_kill_signal, - kAllowedInEnvvar); + kAllowedInEnvvar, + OptionNamespaces::kWatchNamespace); AddOption("--watch-preserve-output", "preserve outputs on watch mode restart", &EnvironmentOptions::watch_mode_preserve_output, - kAllowedInEnvvar); + kAllowedInEnvvar, + false, + OptionNamespaces::kWatchNamespace); Implies("--watch-path", "--watch"); AddOption("--check", "syntax check script without executing", diff --git a/src/node_options.h b/src/node_options.h index e1b633a067fb09..c9c41ae81b1897 100644 --- a/src/node_options.h +++ b/src/node_options.h @@ -415,7 +415,8 @@ std::vector MapAvailableNamespaces(); // Define all namespace entries #define OPTION_NAMESPACE_LIST(V) \ V(kNoNamespace, "") \ - V(kTestRunnerNamespace, "testRunner") + V(kTestRunnerNamespace, "testRunner") \ + V(kWatchNamespace, "watch") enum class OptionNamespaces { #define V(name, _) name, diff --git a/src/node_report.cc b/src/node_report.cc index 0bbf829b45fb61..e4cb681fd76890 100644 --- a/src/node_report.cc +++ b/src/node_report.cc @@ -455,8 +455,7 @@ static Maybe ErrorToString(Isolate* isolate, if (!maybe_str.ToLocal(&js_str)) { return Nothing(); } - String::Utf8Value sv(isolate, js_str); - return Just<>(std::string(*sv, sv.length())); + return Just(Utf8Value(isolate, js_str).ToString()); } static void PrintEmptyJavaScriptStack(JSONWriter* writer) { diff --git a/src/node_sqlite.cc b/src/node_sqlite.cc index cf9ce6686cffca..0b111ce1fb1d59 100644 --- a/src/node_sqlite.cc +++ b/src/node_sqlite.cc @@ -753,6 +753,14 @@ bool DatabaseSync::Open() { CHECK_ERROR_OR_THROW(env()->isolate(), this, r, SQLITE_OK, false); CHECK_EQ(foreign_keys_enabled, open_config_.get_enable_foreign_keys()); + int defensive_enabled; + r = sqlite3_db_config(connection_, + SQLITE_DBCONFIG_DEFENSIVE, + static_cast(open_config_.get_enable_defensive()), + &defensive_enabled); + CHECK_ERROR_OR_THROW(env()->isolate(), this, r, SQLITE_OK, false); + CHECK_EQ(defensive_enabled, open_config_.get_enable_defensive()); + sqlite3_busy_timeout(connection_, open_config_.get_timeout()); if (allow_load_extension_) { @@ -1065,6 +1073,21 @@ void DatabaseSync::New(const FunctionCallbackInfo& args) { allow_unknown_named_params_v.As()->Value()); } } + + Local defensive_v; + if (!options->Get(env->context(), env->defensive_string()) + .ToLocal(&defensive_v)) { + return; + } + if (!defensive_v->IsUndefined()) { + if (!defensive_v->IsBoolean()) { + THROW_ERR_INVALID_ARG_TYPE( + env->isolate(), + "The \"options.defensive\" argument must be a boolean."); + return; + } + open_config.set_enable_defensive(defensive_v.As()->Value()); + } } new DatabaseSync( @@ -1508,8 +1531,7 @@ void DatabaseSync::CreateSession(const FunctionCallbackInfo& args) { } if (table_value->IsString()) { - String::Utf8Value str(env->isolate(), table_value); - table = *str; + table = Utf8Value(env->isolate(), table_value).ToString(); } else { THROW_ERR_INVALID_ARG_TYPE( env->isolate(), "The \"options.table\" argument must be a string."); @@ -1529,8 +1551,7 @@ void DatabaseSync::CreateSession(const FunctionCallbackInfo& args) { return; } if (db_value->IsString()) { - String::Utf8Value str(env->isolate(), db_value); - db_name = std::string(*str); + db_name = Utf8Value(env->isolate(), db_value).ToString(); } else { THROW_ERR_INVALID_ARG_TYPE( env->isolate(), "The \"options.db\" argument must be a string."); @@ -1837,6 +1858,26 @@ void DatabaseSync::EnableLoadExtension( CHECK_ERROR_OR_THROW(isolate, db, load_extension_ret, SQLITE_OK, void()); } +void DatabaseSync::EnableDefensive(const FunctionCallbackInfo& args) { + DatabaseSync* db; + ASSIGN_OR_RETURN_UNWRAP(&db, args.This()); + Environment* env = Environment::GetCurrent(args); + THROW_AND_RETURN_ON_BAD_STATE(env, !db->IsOpen(), "database is not open"); + + auto isolate = args.GetIsolate(); + if (!args[0]->IsBoolean()) { + THROW_ERR_INVALID_ARG_TYPE(isolate, + "The \"active\" argument must be a boolean."); + return; + } + + const int enable = args[0].As()->Value(); + int defensive_enabled; + const int defensive_ret = sqlite3_db_config( + db->connection_, SQLITE_DBCONFIG_DEFENSIVE, enable, &defensive_enabled); + CHECK_ERROR_OR_THROW(isolate, db, defensive_ret, SQLITE_OK, void()); +} + void DatabaseSync::LoadExtension(const FunctionCallbackInfo& args) { DatabaseSync* db; ASSIGN_OR_RETURN_UNWRAP(&db, args.This()); @@ -3318,6 +3359,8 @@ static void Initialize(Local target, db_tmpl, "enableLoadExtension", DatabaseSync::EnableLoadExtension); + SetProtoMethod( + isolate, db_tmpl, "enableDefensive", DatabaseSync::EnableDefensive); SetProtoMethod( isolate, db_tmpl, "loadExtension", DatabaseSync::LoadExtension); SetProtoMethod( diff --git a/src/node_sqlite.h b/src/node_sqlite.h index 862c73bf1cdb46..8f0f9f15d621d5 100644 --- a/src/node_sqlite.h +++ b/src/node_sqlite.h @@ -65,6 +65,10 @@ class DatabaseOpenConfiguration { return allow_unknown_named_params_; } + inline void set_enable_defensive(bool flag) { defensive_ = flag; } + + inline bool get_enable_defensive() const { return defensive_; } + private: std::string location_; bool read_only_ = false; @@ -75,6 +79,7 @@ class DatabaseOpenConfiguration { bool return_arrays_ = false; bool allow_bare_named_params_ = true; bool allow_unknown_named_params_ = false; + bool defensive_ = false; }; class DatabaseSync; @@ -140,6 +145,7 @@ class DatabaseSync : public BaseObject { static void ApplyChangeset(const v8::FunctionCallbackInfo& args); static void EnableLoadExtension( const v8::FunctionCallbackInfo& args); + static void EnableDefensive(const v8::FunctionCallbackInfo& args); static void LoadExtension(const v8::FunctionCallbackInfo& args); static void SetAuthorizer(const v8::FunctionCallbackInfo& args); static int AuthorizerCallback(void* user_data, diff --git a/src/node_util.cc b/src/node_util.cc index 601180905d87c9..d72216fa2add62 100644 --- a/src/node_util.cc +++ b/src/node_util.cc @@ -53,8 +53,8 @@ CHAR_TEST(16, IsUnicodeSurrogateTrail, (ch & 0x400) != 0) static void GetOwnNonIndexProperties( const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - Local context = env->context(); + Isolate* isolate = args.GetIsolate(); + Local context = isolate->GetCurrentContext(); CHECK(args[0]->IsObject()); CHECK(args[1]->IsUint32()); @@ -168,7 +168,7 @@ static void PreviewEntries(const FunctionCallbackInfo& args) { if (!args[0]->IsObject()) return; - Environment* env = Environment::GetCurrent(args); + Isolate* isolate = args.GetIsolate(); bool is_key_value; Local entries; if (!args[0].As()->PreviewEntries(&is_key_value).ToLocal(&entries)) @@ -177,12 +177,8 @@ static void PreviewEntries(const FunctionCallbackInfo& args) { if (args.Length() == 1) return args.GetReturnValue().Set(entries); - Local ret[] = { - entries, - Boolean::New(env->isolate(), is_key_value) - }; - return args.GetReturnValue().Set( - Array::New(env->isolate(), ret, arraysize(ret))); + Local ret[] = {entries, Boolean::New(isolate, is_key_value)}; + return args.GetReturnValue().Set(Array::New(isolate, ret, arraysize(ret))); } static void Sleep(const FunctionCallbackInfo& args) { @@ -222,9 +218,10 @@ static uint32_t GetUVHandleTypeCode(const uv_handle_type type) { } static void GuessHandleType(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); + Isolate* isolate = args.GetIsolate(); + Local context = isolate->GetCurrentContext(); int fd; - if (!args[0]->Int32Value(env->context()).To(&fd)) return; + if (!args[0]->Int32Value(context).To(&fd)) return; CHECK_GE(fd, 0); uv_handle_type t = uv_guess_handle(fd); @@ -239,10 +236,12 @@ static uint32_t FastGuessHandleType(Local receiver, const uint32_t fd) { CFunction fast_guess_handle_type_(CFunction::Make(FastGuessHandleType)); static void ParseEnv(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); + Isolate* isolate = args.GetIsolate(); + Local context = isolate->GetCurrentContext(); + Environment* env = Environment::GetCurrent(context); CHECK_EQ(args.Length(), 1); // content CHECK(args[0]->IsString()); - Utf8Value content(env->isolate(), args[0]); + Utf8Value content(isolate, args[0]); Dotenv dotenv{}; dotenv.ParseContent(content.ToStringView()); Local obj; @@ -252,8 +251,9 @@ static void ParseEnv(const FunctionCallbackInfo& args) { } static void GetCallSites(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - Isolate* isolate = env->isolate(); + Isolate* isolate = args.GetIsolate(); + Local context = isolate->GetCurrentContext(); + Environment* env = Environment::GetCurrent(context); CHECK_EQ(args.Length(), 1); CHECK(args[0]->IsNumber()); @@ -306,8 +306,7 @@ static void GetCallSites(const FunctionCallbackInfo& args) { }; Local callsite; - if (!NewDictionaryInstanceNullProto( - env->context(), callsite_template, values) + if (!NewDictionaryInstanceNullProto(context, callsite_template, values) .ToLocal(&callsite)) { return; } diff --git a/src/node_v8.cc b/src/node_v8.cc index ce253d7fa0a8b1..935ea2cf5157c3 100644 --- a/src/node_v8.cc +++ b/src/node_v8.cc @@ -242,8 +242,8 @@ void UpdateHeapCodeStatisticsBuffer(const FunctionCallbackInfo& args) { void SetFlagsFromString(const FunctionCallbackInfo& args) { CHECK(args[0]->IsString()); - String::Utf8Value flags(args.GetIsolate(), args[0]); - V8::SetFlagsFromString(*flags, static_cast(flags.length())); + Utf8Value flags(args.GetIsolate(), args[0]); + V8::SetFlagsFromString(flags.out(), flags.length()); } void StartCpuProfile(const FunctionCallbackInfo& args) { diff --git a/src/node_version.h b/src/node_version.h index 8698b9e1829ce5..c7f5bdd8a2cdb2 100644 --- a/src/node_version.h +++ b/src/node_version.h @@ -23,13 +23,13 @@ #define SRC_NODE_VERSION_H_ #define NODE_MAJOR_VERSION 25 -#define NODE_MINOR_VERSION 0 -#define NODE_PATCH_VERSION 1 +#define NODE_MINOR_VERSION 1 +#define NODE_PATCH_VERSION 0 #define NODE_VERSION_IS_LTS 0 #define NODE_VERSION_LTS_CODENAME "" -#define NODE_VERSION_IS_RELEASE 0 +#define NODE_VERSION_IS_RELEASE 1 #ifndef NODE_STRINGIFY #define NODE_STRINGIFY(n) NODE_STRINGIFY_HELPER(n) diff --git a/src/permission/permission.cc b/src/permission/permission.cc index 199b36ee76b34c..2d0e1723050e30 100644 --- a/src/permission/permission.cc +++ b/src/permission/permission.cc @@ -21,7 +21,6 @@ using v8::IntegrityLevel; using v8::Local; using v8::MaybeLocal; using v8::Object; -using v8::String; using v8::Value; namespace permission { @@ -34,24 +33,20 @@ static void Has(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); CHECK(args[0]->IsString()); - String::Utf8Value utf8_deny_scope(env->isolate(), args[0]); - if (*utf8_deny_scope == nullptr) { - return; - } - - const std::string deny_scope = *utf8_deny_scope; + const std::string deny_scope = Utf8Value(env->isolate(), args[0]).ToString(); PermissionScope scope = Permission::StringToPermission(deny_scope); if (scope == PermissionScope::kPermissionsRoot) { return args.GetReturnValue().Set(false); } if (args.Length() > 1 && !args[1]->IsUndefined()) { - String::Utf8Value utf8_arg(env->isolate(), args[1]); - if (*utf8_arg == nullptr) { + Utf8Value utf8_arg(env->isolate(), args[1]); + if (utf8_arg.length() == 0) { + args.GetReturnValue().Set(false); return; } return args.GetReturnValue().Set( - env->permission()->is_granted(env, scope, *utf8_arg)); + env->permission()->is_granted(env, scope, utf8_arg.ToStringView())); } return args.GetReturnValue().Set(env->permission()->is_granted(env, scope)); diff --git a/src/string_bytes.cc b/src/string_bytes.cc index d78bbb237325d2..03b5fd7ebe3816 100644 --- a/src/string_bytes.cc +++ b/src/string_bytes.cc @@ -304,10 +304,10 @@ size_t StringBytes::Write(Isolate* isolate, input_view.length()); } } else { - String::Value value(isolate, str); + TwoByteValue value(isolate, str); size_t written_len = buflen; auto result = simdutf::base64_to_binary_safe( - reinterpret_cast(*value), + reinterpret_cast(value.out()), value.length(), buf, written_len, @@ -343,10 +343,10 @@ size_t StringBytes::Write(Isolate* isolate, input_view.length()); } } else { - String::Value value(isolate, str); + TwoByteValue value(isolate, str); size_t written_len = buflen; auto result = simdutf::base64_to_binary_safe( - reinterpret_cast(*value), + reinterpret_cast(value.out()), value.length(), buf, written_len); @@ -368,8 +368,8 @@ size_t StringBytes::Write(Isolate* isolate, reinterpret_cast(input_view.data8()), input_view.length()); } else { - String::Value value(isolate, str); - nbytes = nbytes::HexDecode(buf, buflen, *value, value.length()); + TwoByteValue value(isolate, str); + nbytes = nbytes::HexDecode(buf, buflen, value.out(), value.length()); } break; diff --git a/src/util-inl.h b/src/util-inl.h index 4e9c4fea85d12e..d07bceb425f008 100644 --- a/src/util-inl.h +++ b/src/util-inl.h @@ -359,7 +359,7 @@ v8::MaybeLocal ToV8Value(v8::Local context, v8::MaybeLocal ToV8Value(v8::Local context, std::u16string_view str, v8::Isolate* isolate) { - if (isolate == nullptr) isolate = context->GetIsolate(); + if (isolate == nullptr) isolate = v8::Isolate::GetCurrent(); if (str.length() >= static_cast(v8::String::kMaxLength)) [[unlikely]] { // V8 only has a TODO comment about adding an exception when the maximum diff --git a/src/uv.cc b/src/uv.cc index d4fadfc24e9a17..a005fd05c8f49c 100644 --- a/src/uv.cc +++ b/src/uv.cc @@ -68,7 +68,9 @@ void GetErrMessage(const FunctionCallbackInfo& args) { } void ErrName(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); + Isolate* isolate = args.GetIsolate(); + Local context = isolate->GetCurrentContext(); + Environment* env = Environment::GetCurrent(context); if (env->options()->pending_deprecation && env->EmitErrNameWarning()) { if (ProcessEmitDeprecationWarning( env, @@ -82,13 +84,12 @@ void ErrName(const FunctionCallbackInfo& args) { CHECK_LT(err, 0); char name[50]; uv_err_name_r(err, name, sizeof(name)); - args.GetReturnValue().Set(OneByteString(env->isolate(), name)); + args.GetReturnValue().Set(OneByteString(isolate, name)); } void GetErrMap(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - Isolate* isolate = env->isolate(); - Local context = env->context(); + Isolate* isolate = args.GetIsolate(); + Local context = isolate->GetCurrentContext(); // This can't return a SafeMap, because the uv binding can be referenced // by user code by using `process.binding('uv').getErrorMap()`: diff --git a/test/addons/async-resource/test.js b/test/addons/async-resource/test.js index 86cbb38e1cf2df..d7bc44a92be827 100644 --- a/test/addons/async-resource/test.js +++ b/test/addons/async-resource/test.js @@ -17,7 +17,7 @@ let after = 0; let destroy = 0; async_hooks.createHook({ - init(id, type, triggerAsyncId, resource) { + init: common.mustCall((id, type, triggerAsyncId, resource) => { assert.strictEqual(typeof id, 'number'); assert.strictEqual(typeof resource, 'object'); assert(id > 1); @@ -26,7 +26,7 @@ async_hooks.createHook({ assert.strictEqual(triggerAsyncId, expectedTriggerId); bindingUids.push(id); } - }, + }, 7), before(id) { if (bindingUids.includes(id)) before++; @@ -48,8 +48,11 @@ for (const call of [binding.callViaFunction, let uid; const object = { methöd(arg) { + // eslint-disable-next-line node-core/must-call-assert assert.strictEqual(this, object); + // eslint-disable-next-line node-core/must-call-assert assert.strictEqual(arg, 42); + // eslint-disable-next-line node-core/must-call-assert assert.strictEqual(async_hooks.executionAsyncId(), uid); return 'baz'; }, diff --git a/test/addons/cppgc-object/test.js b/test/addons/cppgc-object/test.js index e18b6e17b88df9..acdb145ae721c4 100644 --- a/test/addons/cppgc-object/test.js +++ b/test/addons/cppgc-object/test.js @@ -23,7 +23,7 @@ for (let i = 0; i < count; ++i) { globalThis.gc(); -setTimeout(async function() { +setTimeout(common.mustCall(() => (async function() { // GC should have invoked Trace() on at least some of the CppGCed objects, // but they should all be alive at this point. assert.strictEqual(states[kDestructCount], 0); @@ -48,4 +48,4 @@ setTimeout(async function() { 'All old CppGCed are destroyed', () => states[kDestructCount] === count * 2, ); -}, 1); +})().then(common.mustCall())), 1); diff --git a/test/addons/make-callback-recurse/test.js b/test/addons/make-callback-recurse/test.js index 4a540003acd8d1..6b927ffbfdba48 100644 --- a/test/addons/make-callback-recurse/test.js +++ b/test/addons/make-callback-recurse/test.js @@ -71,11 +71,11 @@ assert.throws(() => { if (arg === 1) { // The tests are first run on bootstrap during LoadEnvironment() in // src/node.cc. Now run the tests through node::MakeCallback(). - setImmediate(() => { + setImmediate(common.mustCall(() => { makeCallback({}, common.mustCall(() => { verifyExecutionOrder(2); })); - }); + })); } else if (arg === 2) { // Make sure there are no conflicts using node::MakeCallback() // within timers. diff --git a/test/addons/no-addons/permission.js b/test/addons/no-addons/permission.js index 1d1bbf6e95468e..a487c8a6c9b2af 100644 --- a/test/addons/no-addons/permission.js +++ b/test/addons/no-addons/permission.js @@ -7,37 +7,11 @@ const assert = require('assert'); const bindingPath = require.resolve(`./build/${common.buildType}/binding`); -const assertError = (error) => { - assert(error instanceof Error); - assert.strictEqual(error.code, 'ERR_DLOPEN_DISABLED'); - assert.strictEqual( - error.message, - 'Cannot load native addon because loading addons is disabled.', - ); -}; - -{ - let threw = false; - - try { - require(bindingPath); - } catch (error) { - assertError(error); - threw = true; - } - - assert(threw); -} - -{ - let threw = false; - - try { - process.dlopen({ exports: {} }, bindingPath); - } catch (error) { - assertError(error); - threw = true; - } - - assert(threw); -} +assert.throws(() => require(bindingPath), { + code: 'ERR_DLOPEN_DISABLED', + message: 'Cannot load native addon because loading addons is disabled.', +}); +assert.throws(() => process.dlopen({ exports: {} }, bindingPath), { + code: 'ERR_DLOPEN_DISABLED', + message: 'Cannot load native addon because loading addons is disabled.', +}); diff --git a/test/addons/no-addons/test-worker.js b/test/addons/no-addons/test-worker.js index 25e893ca33aea7..fd932d64f440aa 100644 --- a/test/addons/no-addons/test-worker.js +++ b/test/addons/no-addons/test-worker.js @@ -9,13 +9,13 @@ const { Worker } = require('worker_threads'); const binding = path.resolve(__dirname, `./build/${common.buildType}/binding`); -const assertError = (error) => { +const assertError = common.mustCall((error) => { assert.strictEqual(error.code, 'ERR_DLOPEN_DISABLED'); assert.strictEqual( error.message, 'Cannot load native addon because loading addons is disabled.', ); -}; +}, 4); { // Flags should be inherited @@ -23,7 +23,7 @@ const assertError = (error) => { eval: true, }); - worker.on('error', common.mustCall(assertError)); + worker.on('error', assertError); } { @@ -35,7 +35,7 @@ const assertError = (error) => { }, ); - worker.on('error', common.mustCall(assertError)); + worker.on('error', assertError); } { @@ -45,7 +45,7 @@ const assertError = (error) => { execArgv: ['--no-addons'], }); - worker.on('error', common.mustCall(assertError)); + worker.on('error', assertError); } { @@ -55,5 +55,5 @@ const assertError = (error) => { execArgv: [], }); - worker.on('error', common.mustCall(assertError)); + worker.on('error', assertError); } diff --git a/test/addons/no-addons/test.js b/test/addons/no-addons/test.js index 485fad4a8bc643..7b93806d6db046 100644 --- a/test/addons/no-addons/test.js +++ b/test/addons/no-addons/test.js @@ -7,37 +7,11 @@ const assert = require('assert'); const bindingPath = require.resolve(`./build/${common.buildType}/binding`); -const assertError = (error) => { - assert(error instanceof Error); - assert.strictEqual(error.code, 'ERR_DLOPEN_DISABLED'); - assert.strictEqual( - error.message, - 'Cannot load native addon because loading addons is disabled.', - ); -}; - -{ - let threw = false; - - try { - require(bindingPath); - } catch (error) { - assertError(error); - threw = true; - } - - assert(threw); -} - -{ - let threw = false; - - try { - process.dlopen({ exports: {} }, bindingPath); - } catch (error) { - assertError(error); - threw = true; - } - - assert(threw); -} +assert.throws(() => require(bindingPath), { + code: 'ERR_DLOPEN_DISABLED', + message: 'Cannot load native addon because loading addons is disabled.', +}); +assert.throws(() => process.dlopen({ exports: {} }, bindingPath), { + code: 'ERR_DLOPEN_DISABLED', + message: 'Cannot load native addon because loading addons is disabled.', +}); diff --git a/test/addons/null-buffer-neuter/test.js b/test/addons/null-buffer-neuter/test.js index d99690542a4d84..7a3ba1535ec9da 100644 --- a/test/addons/null-buffer-neuter/test.js +++ b/test/addons/null-buffer-neuter/test.js @@ -6,6 +6,6 @@ const binding = require(`./build/${common.buildType}/binding`); binding.run(); global.gc(); -setImmediate(() => { +setImmediate(common.mustCall(() => { assert.strictEqual(binding.isAlive(), 0); -}); +})); diff --git a/test/addons/symlinked-module/submodule.js b/test/addons/symlinked-module/submodule.js index d4b59e9efa4f5e..79400ecf4c10c8 100644 --- a/test/addons/symlinked-module/submodule.js +++ b/test/addons/symlinked-module/submodule.js @@ -1,13 +1,13 @@ 'use strict'; -require('../../common'); +const common = require('../../common'); const path = require('path'); const assert = require('assert'); // This is a subtest of symlinked-module/test.js. This is not // intended to be run directly. -module.exports.test = function test(bindingDir) { +module.exports.test = common.mustCall(function test(bindingDir) { const mod = require(path.join(bindingDir, 'binding.node')); assert.notStrictEqual(mod, null); assert.strictEqual(mod.hello(), 'world'); -}; +}, require.main === module ? 0 : 2); diff --git a/test/cctest/inspector/test_network_requests_buffer.cc b/test/cctest/inspector/test_network_requests_buffer.cc new file mode 100644 index 00000000000000..40461e95ff490e --- /dev/null +++ b/test/cctest/inspector/test_network_requests_buffer.cc @@ -0,0 +1,181 @@ +#include "gtest/gtest.h" +#include "inspector/network_requests_buffer.h" + +namespace node { +namespace inspector { + +static uint8_t kDummyData[] = "long enough dummy data suitable for tests."; + +TEST(RequestEntry, BufferSize) { + auto entry = RequestEntry(0, Charset::kBinary, false, 10); + EXPECT_EQ(entry.buffer_size(), 0u); + entry.push_request_data_blob(protocol::Binary::fromSpan(kDummyData, 5)); + EXPECT_EQ(entry.buffer_size(), 5u); + entry.push_response_data_blob(protocol::Binary::fromSpan(kDummyData, 3)); + EXPECT_EQ(entry.buffer_size(), 8u); + + // Exceeds the per-resource buffer size limit, ignore it. + entry.push_request_data_blob(protocol::Binary::fromSpan(kDummyData, 3)); + EXPECT_EQ(entry.buffer_size(), 8u); + entry.push_response_data_blob(protocol::Binary::fromSpan(kDummyData, 3)); + EXPECT_EQ(entry.buffer_size(), 8u); + + entry.clear_response_data_blobs(); + EXPECT_EQ(entry.buffer_size(), 5u); +} + +TEST(RequestsBuffer, Basic) { + RequestsBuffer buffer(10); + EXPECT_FALSE(buffer.contains("1")); + EXPECT_TRUE( + buffer.emplace("1", RequestEntry(0, Charset::kBinary, false, 10))); + EXPECT_TRUE(buffer.contains("1")); + + // Duplicate entry, ignore it. + EXPECT_FALSE( + buffer.emplace("1", RequestEntry(0, Charset::kBinary, false, 10))); + EXPECT_TRUE(buffer.contains("1")); + + EXPECT_TRUE( + buffer.emplace("2", RequestEntry(0, Charset::kBinary, false, 10))); + EXPECT_TRUE(buffer.contains("2")); + + buffer.erase("1"); + EXPECT_FALSE(buffer.contains("1")); + EXPECT_TRUE(buffer.contains("2")); +} + +TEST(RequestsBuffer, Find) { + RequestsBuffer buffer(10); + EXPECT_FALSE(buffer.contains("1")); + EXPECT_TRUE( + buffer.emplace("1", RequestEntry(0, Charset::kBinary, false, 10))); + EXPECT_TRUE(buffer.contains("1")); + + { + auto it = buffer.find("1"); + EXPECT_NE(it, buffer.end()); + EXPECT_EQ(it->first, "1"); + EXPECT_EQ(it->second.buffer_size(), 0u); + it->second.push_request_data_blob( + protocol::Binary::fromSpan(kDummyData, 5)); + it->second.push_response_data_blob( + protocol::Binary::fromSpan(kDummyData, 5)); + EXPECT_EQ(it->second.buffer_size(), 10u); + + EXPECT_EQ(buffer.total_buffer_size(), 0u); + // Update the total buffer size when the iterator is destroyed. + } + EXPECT_EQ(buffer.total_buffer_size(), 10u); + + // Shrink + { + auto it = buffer.find("1"); + EXPECT_NE(it, buffer.end()); + EXPECT_EQ(it->first, "1"); + EXPECT_EQ(it->second.buffer_size(), 10u); + it->second.clear_response_data_blobs(); + EXPECT_EQ(it->second.buffer_size(), 5u); + } + EXPECT_EQ(buffer.total_buffer_size(), 5u); + + // const find + { + auto it = buffer.cfind("1"); + EXPECT_NE(it, buffer.end()); + EXPECT_EQ(it->first, "1"); + EXPECT_EQ(it->second.buffer_size(), 5u); + } + + // Non-existing entry. + { + auto it = buffer.find("2"); + EXPECT_EQ(it, buffer.end()); + } + { + auto it = buffer.cfind("2"); + EXPECT_EQ(it, buffer.end()); + } +} + +TEST(RequestsBuffer, Erase) { + RequestsBuffer buffer(10); + EXPECT_FALSE(buffer.contains("1")); + EXPECT_TRUE( + buffer.emplace("1", RequestEntry(0, Charset::kBinary, false, 10))); + EXPECT_TRUE(buffer.contains("1")); + + { + auto it = buffer.find("1"); + EXPECT_NE(it, buffer.end()); + EXPECT_EQ(it->first, "1"); + EXPECT_EQ(it->second.buffer_size(), 0u); + it->second.push_request_data_blob( + protocol::Binary::fromSpan(kDummyData, 5)); + it->second.push_response_data_blob( + protocol::Binary::fromSpan(kDummyData, 5)); + EXPECT_EQ(it->second.buffer_size(), 10u); + + EXPECT_EQ(buffer.total_buffer_size(), 0u); + // Update the total buffer size when the iterator is destroyed. + } + EXPECT_EQ(buffer.total_buffer_size(), 10u); + + buffer.erase("1"); + EXPECT_FALSE(buffer.contains("1")); + EXPECT_EQ(buffer.total_buffer_size(), 0u); + + // Non-existing entry. + buffer.erase("2"); + EXPECT_EQ(buffer.total_buffer_size(), 0u); +} + +TEST(RequestsBuffer, EnforceLimit) { + RequestsBuffer buffer(15); + { + EXPECT_TRUE( + buffer.emplace("1", RequestEntry(0, Charset::kBinary, false, 15))); + buffer.find("1")->second.push_request_data_blob( + protocol::Binary::fromSpan(kDummyData, 10)); + EXPECT_EQ(buffer.total_buffer_size(), 10u); + } + { + EXPECT_TRUE( + buffer.emplace("2", RequestEntry(0, Charset::kBinary, false, 15))); + buffer.find("2")->second.push_request_data_blob( + protocol::Binary::fromSpan(kDummyData, 5)); + EXPECT_EQ(buffer.total_buffer_size(), 15u); + } + + // Exceeds the limit, the oldest entry is removed. + { + EXPECT_TRUE( + buffer.emplace("3", RequestEntry(0, Charset::kBinary, false, 15))); + buffer.find("3")->second.push_request_data_blob( + protocol::Binary::fromSpan(kDummyData, 5)); + // "2" and "3" are kept. + EXPECT_EQ(buffer.total_buffer_size(), 10u); + } + + EXPECT_FALSE(buffer.contains("1")); + EXPECT_TRUE(buffer.contains("2")); + EXPECT_TRUE(buffer.contains("3")); + + // Exceeds the limit, the oldest entry is removed. + { + EXPECT_TRUE( + buffer.emplace("4", RequestEntry(0, Charset::kBinary, false, 15))); + buffer.find("4")->second.push_request_data_blob( + protocol::Binary::fromSpan(kDummyData, 10)); + // "3" and "4" are kept. + EXPECT_EQ(buffer.total_buffer_size(), 15u); + } + + EXPECT_FALSE(buffer.contains("1")); + EXPECT_FALSE(buffer.contains("2")); + EXPECT_TRUE(buffer.contains("3")); + EXPECT_TRUE(buffer.contains("4")); +} + +} // namespace inspector +} // namespace node diff --git a/test/client-proxy/test-https-proxy-request-invalid-char-in-url.mjs b/test/client-proxy/test-https-proxy-request-invalid-char-in-url.mjs index b5e16e8254f553..80c7be4931cd57 100644 --- a/test/client-proxy/test-https-proxy-request-invalid-char-in-url.mjs +++ b/test/client-proxy/test-https-proxy-request-invalid-char-in-url.mjs @@ -82,7 +82,19 @@ for (const testCase of testCases) { proxy.close(); server.close(); assert.deepStrictEqual(requests, expectedUrls); - assert.deepStrictEqual(new Set(logs), expectedProxyLogs); + const logSet = new Set(logs); + for (const log of logSet) { + if (log.source === 'proxy connect' && log.error?.code === 'EPIPE') { + // There can be a race from eagerly shutting down the servers and severing + // two pipes at the same time but for the purpose of this test, we only + // care about whether the requests are initiated from the client as expected, + // not how the upstream/proxy servers behave. Ignore EPIPE errors from them.. + // Refs: https://github.com/nodejs/node/issues/59741 + console.log('Ignoring EPIPE error from proxy connect', log.error); + logSet.delete(log); + } + } + assert.deepStrictEqual(logSet, expectedProxyLogs); })); } })); diff --git a/test/common/assertSnapshot.js b/test/common/assertSnapshot.js index 16509f12ee89fc..0e350cd1dac6fa 100644 --- a/test/common/assertSnapshot.js +++ b/test/common/assertSnapshot.js @@ -4,6 +4,7 @@ const path = require('node:path'); const test = require('node:test'); const fs = require('node:fs/promises'); const assert = require('node:assert/strict'); +const { hostname } = require('node:os'); const stackFramesRegexp = /(?<=\n)(\s+)((.+?)\s+\()?(?:\(?(.+?):(\d+)(?::(\d+))?)\)?(\s+\{)?(\[\d+m)?(\n|$)/g; const windowNewlineRegexp = /\r/g; @@ -100,6 +101,97 @@ async function spawnAndAssert(filename, transform = (x) => x, { tty = false, ... await assertSnapshot(transform(`${stdout}${stderr}`), filename); } +function replaceTestDuration(str) { + return str + .replaceAll(/duration_ms: [0-9.]+/g, 'duration_ms: *') + .replaceAll(/duration_ms [0-9.]+/g, 'duration_ms *'); +} + +const root = path.resolve(__dirname, '..', '..'); +const color = '(\\[\\d+m)'; +const stackTraceBasePath = new RegExp(`${color}\\(${root.replaceAll(/[\\^$*+?.()|[\]{}]/g, '\\$&')}/?${color}(.*)${color}\\)`, 'g'); + +function replaceSpecDuration(str) { + return str + .replaceAll(/[0-9.]+ms/g, '*ms') + .replaceAll(/duration_ms [0-9.]+/g, 'duration_ms *') + .replace(stackTraceBasePath, '$3'); +} + +function replaceJunitDuration(str) { + return str + .replaceAll(/time="[0-9.]+"/g, 'time="*"') + .replaceAll(/duration_ms [0-9.]+/g, 'duration_ms *') + .replaceAll(`hostname="${hostname()}"`, 'hostname="HOSTNAME"') + .replaceAll(/file="[^"]*"/g, 'file="*"') + .replace(stackTraceBasePath, '$3'); +} + +function removeWindowsPathEscaping(str) { + return common.isWindows ? str.replaceAll(/\\\\/g, '\\') : str; +} + +function replaceTestLocationLine(str) { + return str.replaceAll(/(js:)(\d+)(:\d+)/g, '$1(LINE)$3'); +} + +// The Node test coverage returns results for all files called by the test. This +// will make the output file change if files like test/common/index.js change. +// This transform picks only the first line and then the lines from the test +// file. +function pickTestFileFromLcov(str) { + const lines = str.split(/\n/); + const firstLineOfTestFile = lines.findIndex( + (line) => line.startsWith('SF:') && line.trim().endsWith('output.js'), + ); + const lastLineOfTestFile = lines.findIndex( + (line, index) => index > firstLineOfTestFile && line.trim() === 'end_of_record', + ); + return ( + lines[0] + '\n' + lines.slice(firstLineOfTestFile, lastLineOfTestFile + 1).join('\n') + '\n' + ); +} + +const defaultTransform = transform( + replaceWindowsLineEndings, + replaceStackTrace, + removeWindowsPathEscaping, + transformProjectRoot(), + replaceWindowsPaths, + replaceTestDuration, + replaceTestLocationLine, +); +const specTransform = transform( + replaceSpecDuration, + replaceWindowsLineEndings, + replaceStackTrace, + replaceWindowsPaths, +); +const junitTransform = transform( + replaceJunitDuration, + replaceWindowsLineEndings, + replaceStackTrace, + replaceWindowsPaths, +); +const lcovTransform = transform( + replaceWindowsLineEndings, + replaceStackTrace, + transformProjectRoot(), + replaceWindowsPaths, + pickTestFileFromLcov, +); + +function ensureCwdIsProjectRoot() { + if (process.cwd() !== root) { + process.chdir(root); + } +} + +function canColorize() { + // Loading it lazily to avoid breaking `NODE_REGENERATE_SNAPSHOTS`. + return require('internal/tty').getColorDepth() > 2; +} + module.exports = { assertSnapshot, getSnapshotPath, @@ -111,4 +203,11 @@ module.exports = { spawnAndAssert, transform, transformProjectRoot, + replaceTestDuration, + defaultTransform, + specTransform, + junitTransform, + lcovTransform, + ensureCwdIsProjectRoot, + canColorize, }; diff --git a/test/common/debugger.js b/test/common/debugger.js index 1a258913e00dd7..f5a47cbe06ea71 100644 --- a/test/common/debugger.js +++ b/test/common/debugger.js @@ -7,12 +7,10 @@ const BREAK_MESSAGE = new RegExp('(?:' + [ 'exception', 'other', 'promiseRejection', 'step', ].join('|') + ') in', 'i'); -// Some macOS machines require more time to receive the outputs from the client. let TIMEOUT = common.platformTimeout(10000); -if (common.isWindows) { - // Some of the windows machines in the CI need more time to receive - // the outputs from the client. - // https://github.com/nodejs/build/issues/3014 +// Some macOS and Windows machines require more time to receive the outputs from the client. +// https://github.com/nodejs/build/issues/3014 +if (common.isWindows || common.isMacOS) { TIMEOUT = common.platformTimeout(15000); } diff --git a/test/common/index.js b/test/common/index.js index 61b82e28f53064..83166076e16405 100755 --- a/test/common/index.js +++ b/test/common/index.js @@ -54,6 +54,7 @@ const noop = () => {}; const hasCrypto = Boolean(process.versions.openssl) && !process.env.NODE_SKIP_CRYPTO; +const hasInspector = Boolean(process.features.inspector); const hasSQLite = Boolean(process.versions.sqlite); const hasQuic = hasCrypto && !!process.features.quic; @@ -711,7 +712,7 @@ function expectsError(validator, exact) { } function skipIfInspectorDisabled() { - if (!process.features.inspector) { + if (!hasInspector) { skip('V8 inspector is disabled'); } } @@ -930,6 +931,7 @@ const common = { hasIntl, hasCrypto, hasQuic, + hasInspector, hasSQLite, invalidArgTypeHelper, isAlive, diff --git a/test/common/index.mjs b/test/common/index.mjs index 98a26e29ddad27..b535f38ae32673 100644 --- a/test/common/index.mjs +++ b/test/common/index.mjs @@ -17,6 +17,7 @@ const { getTTYfd, hasCrypto, hasQuic, + hasInspector, hasSQLite, hasIntl, hasIPv6, @@ -68,6 +69,7 @@ export { getTTYfd, hasCrypto, hasQuic, + hasInspector, hasSQLite, hasIntl, hasIPv6, diff --git a/test/common/watch.js b/test/common/watch.js new file mode 100644 index 00000000000000..068790780e1470 --- /dev/null +++ b/test/common/watch.js @@ -0,0 +1,16 @@ +'use strict'; +const common = require('./index.js'); + +exports.skipIfNoWatchModeSignals = function() { + if (common.isWindows) { + common.skip('no signals on Windows'); + } + + if (common.isIBMi) { + common.skip('IBMi does not support `fs.watch()`'); + } + + if (common.isAIX) { + common.skip('folder watch capability is limited in AIX.'); + } +}; diff --git a/test/common/websocket-server.js b/test/common/websocket-server.js index 7f2447396972f7..f120d3bf71bb03 100644 --- a/test/common/websocket-server.js +++ b/test/common/websocket-server.js @@ -9,10 +9,12 @@ class WebSocketServer { constructor({ port = 0, server, + customHandleUpgradeHeaders = [], }) { this.port = port; this.server = server || http.createServer(); this.clients = new Set(); + this.customHandleUpgradeHeaders = customHandleUpgradeHeaders; this.server.on('upgrade', this.handleUpgrade.bind(this)); } @@ -36,6 +38,7 @@ class WebSocketServer { 'Upgrade: websocket', 'Connection: Upgrade', `Sec-WebSocket-Accept: ${acceptKey}`, + ...this.customHandleUpgradeHeaders, ]; socket.write(responseHeaders.join('\r\n') + '\r\n\r\n'); diff --git a/test/common/wpt.js b/test/common/wpt.js index a21ad3363e00cb..6c9a75e4e4cc53 100644 --- a/test/common/wpt.js +++ b/test/common/wpt.js @@ -412,6 +412,9 @@ class BuildRequirement { // Not using common.hasCrypto because of the global leak checks this.hasCrypto = Boolean(process.versions.openssl) && !process.env.NODE_SKIP_CRYPTO; + + // Not using common.hasInspector because of the global leak checks + this.hasInspector = Boolean(process.features.inspector); } /** @@ -429,6 +432,9 @@ class BuildRequirement { if (requires.has('crypto') && !this.hasCrypto) { return 'crypto'; } + if (requires.has('inspector') && !this.hasInspector) { + return 'inspector'; + } return false; } } diff --git a/test/es-module/test-esm-loader-modulemap.js b/test/es-module/test-esm-loader-modulemap.js index 83125fce738139..f581c7f507640e 100644 --- a/test/es-module/test-esm-loader-modulemap.js +++ b/test/es-module/test-esm-loader-modulemap.js @@ -16,7 +16,7 @@ const jsonModuleDataUrl = 'data:application/json,""'; const stubJsModule = createDynamicModule([], ['default'], jsModuleDataUrl); const stubJsonModule = createDynamicModule([], ['default'], jsonModuleDataUrl); -const loader = createModuleLoader(false); +const loader = createModuleLoader(); const jsModuleJob = new ModuleJob(loader, stubJsModule.module, undefined, () => new Promise(() => {})); const jsonModuleJob = new ModuleJob(loader, stubJsonModule.module, diff --git a/test/eslint.config_partial.mjs b/test/eslint.config_partial.mjs index 915a8b97b347ef..0980ffac7a28a1 100644 --- a/test/eslint.config_partial.mjs +++ b/test/eslint.config_partial.mjs @@ -156,7 +156,21 @@ export default [ }, { files: [ - 'test/{async-hooks,benchmark,cctest,client-proxy,message,module-hooks,node-api,pummel,pseudo-tty,v8-updates,wasi}/**/*.{js,mjs,cjs}', + `test/{${[ + 'abort', + 'addons', + 'async-hooks', + 'benchmark', + 'cctest', + 'client-proxy', + 'message', + 'module-hooks', + 'node-api', + 'pummel', + 'pseudo-tty', + 'v8-updates', + 'wasi', + ].join(',')}}/**/*.{js,mjs,cjs}`, ], rules: { 'node-core/must-call-assert': 'error', diff --git a/test/fixtures/crypto/ecdsa.js b/test/fixtures/crypto/ecdsa.js index b8827b24d41965..c3545fb20ec7df 100644 --- a/test/fixtures/crypto/ecdsa.js +++ b/test/fixtures/crypto/ecdsa.js @@ -72,18 +72,20 @@ module.exports = function() { 'b6a0a14d7e4bc6dd2eda82c9234f174b670b60c8f7d101f68fdf5889e02373b025' + 'dcbc4c82f2929b8e06c68535da98e38fe399c53a814b097935581ef21535eb', 'hex'), - 'SHA3-256': Buffer.from( - 'f6a48eb5557f484ed0c3e4b5c78a3cf497cbd346db06a4165d429248aa2cc51a69' + - '747d09f57af145469a8b607a9b8b9709629d74e8f5ca337c6ddc581b6f6103', - 'hex'), - 'SHA3-384': Buffer.from( - '777785978eb59da32888554dc7fd62d1ba1a3033cddaa8c36b8f3dcea8f85e1c8e' + - '6db26f509747bd144dfa9436784bf4abbcaa6abcf1ecc09cea3b921d46738c', - 'hex'), - 'SHA3-512': Buffer.from( - '0f01c2083b5dd7fccb2784563f88cd9a815d570a1690695e426643ab725780760d' + - 'e972e26e18d67f5557be89f17b4cd0065ce2937de299bdb2e972ebf7635084', - 'hex') + ...(!process.features.openssl_is_boringssl ? { + 'SHA3-256': Buffer.from( + 'f6a48eb5557f484ed0c3e4b5c78a3cf497cbd346db06a4165d429248aa2cc51a69' + + '747d09f57af145469a8b607a9b8b9709629d74e8f5ca337c6ddc581b6f6103', + 'hex'), + 'SHA3-384': Buffer.from( + '777785978eb59da32888554dc7fd62d1ba1a3033cddaa8c36b8f3dcea8f85e1c8e' + + '6db26f509747bd144dfa9436784bf4abbcaa6abcf1ecc09cea3b921d46738c', + 'hex'), + 'SHA3-512': Buffer.from( + '0f01c2083b5dd7fccb2784563f88cd9a815d570a1690695e426643ab725780760d' + + 'e972e26e18d67f5557be89f17b4cd0065ce2937de299bdb2e972ebf7635084', + 'hex') + } : {}) }, 'P-384': { 'SHA-1': Buffer.from( @@ -102,18 +104,20 @@ module.exports = function() { '72fbdb369fd34c1c54264d07f4facd69b02e4206f8a8bb259b882a305c56fde2d3' + '5107e493c53cd6b4af0b31306f4d03fd43cfc762a1030e17a3d775453a1212b142' + '9f7b3d93066a5f42a10b138cd177dc09616e827d598822d78d4627b754e6', 'hex'), - 'SHA3-256': Buffer.from( - '0b07c078be30fa5925a307d6fc559c5f398e63fb5d007d6b24a834847f2d3d18d5' + - 'b5e840711c52a7bc6626c3ced93301e873c013a706f6b297c12cc6d47a71e0529e' + - '719f43957de9995621d3cb0217469adaa6fd3135470771d0aa9d05d7a9c6', 'hex'), - 'SHA3-384': Buffer.from( - '2f36e8b04af46f68ef900c2720e3518b06f5440865d44072bbad5d62288c575042' + - 'b183a372acd70328c738668dcecb9866801462d62df3c35450fdc6c95433103fcd' + - 'c77999b640e3f92bd4e9be6e27ab129d1bc4f0b2a4c829388666920892d3', 'hex'), - 'SHA3-512': Buffer.from( - '32a951e886c33ac57a008efe9643bc92aa3ece9521d115e0c7240caecf124d1f7c' + - 'dcba7fabb9ad5202e04f7aa591ab01ed3f060f04f493e4f24430fe8159200612f0' + - '2849108b8be6edc8494c328097ad9265928efe5cb9d91be2f013ee17ee4e', 'hex') + ...(!process.features.openssl_is_boringssl ? { + 'SHA3-256': Buffer.from( + '0b07c078be30fa5925a307d6fc559c5f398e63fb5d007d6b24a834847f2d3d18d5' + + 'b5e840711c52a7bc6626c3ced93301e873c013a706f6b297c12cc6d47a71e0529e' + + '719f43957de9995621d3cb0217469adaa6fd3135470771d0aa9d05d7a9c6', 'hex'), + 'SHA3-384': Buffer.from( + '2f36e8b04af46f68ef900c2720e3518b06f5440865d44072bbad5d62288c575042' + + 'b183a372acd70328c738668dcecb9866801462d62df3c35450fdc6c95433103fcd' + + 'c77999b640e3f92bd4e9be6e27ab129d1bc4f0b2a4c829388666920892d3', 'hex'), + 'SHA3-512': Buffer.from( + '32a951e886c33ac57a008efe9643bc92aa3ece9521d115e0c7240caecf124d1f7c' + + 'dcba7fabb9ad5202e04f7aa591ab01ed3f060f04f493e4f24430fe8159200612f0' + + '2849108b8be6edc8494c328097ad9265928efe5cb9d91be2f013ee17ee4e', 'hex') + } : {}) }, 'P-521': { 'SHA-1': Buffer.from( @@ -140,29 +144,35 @@ module.exports = function() { '01f0071e6a32867fa70f695cd39c4e87e142b9e4134d38740bd6fee354a575167e' + '13524e94832637910fe11e53a85fb21b91adb81bb1779c4e2b8bc87c717dc35084', 'hex'), - 'SHA3-256': Buffer.from( - '00463679f47a4c705e03447360dcf34d1743e0d4b2591cc66832a6bc80d92e538c' + - '169a1fd330f98e7235ca7fec7e16ac44fb13095b8edf2c76b75c4845177d59e425' + - '0127c4359f6a4c9ccb63e7a9ff8122c0b4a8b7408e28c96817ecc3baf8c559c413' + - 'c3bb580447dec9f52139b2afde369cd51730f050bc94137556ae137f0509464219', - 'hex'), - 'SHA3-384': Buffer.from( - '01969a4db0888bc067a68a31fe5d0fc97e0b701f570565f7b25cb27707c6f020ff' + - '680f8553ec5c2d6885e9e91b39262ed1bde375525eb13fdf12089b7939c7689735' + - '0101c8b8d1129a217e8e956bef78cf7b9a0458523b04ac8e0b84ce73d54326f7a8' + - '704ee42fe183f3ef79d83e676f34dc5476e2342641a5b973d3d94e8503676fbbc5', - 'hex'), - 'SHA3-512': Buffer.from( - '000f362e914ee0136663cf57bf4085c25604af6dc198b4818751e1195ee7e41a16' + - '91be909dcbc2bae00b8917f6bb918eae3740ac1b76e0913137c2da1171d6400b55' + - '01ec6e1dc5987a27fe16fc2ce5c8e954088f898a9bbefb176eaa8bbd9ccc264c4c' + - 'cc38c83ac8b5a168f90228daf8405a2b9bf7829c263a646b4e1098e2ace38deec7', - 'hex') + ...(!process.features.openssl_is_boringssl ? { + 'SHA3-256': Buffer.from( + '00463679f47a4c705e03447360dcf34d1743e0d4b2591cc66832a6bc80d92e538c' + + '169a1fd330f98e7235ca7fec7e16ac44fb13095b8edf2c76b75c4845177d59e425' + + '0127c4359f6a4c9ccb63e7a9ff8122c0b4a8b7408e28c96817ecc3baf8c559c413' + + 'c3bb580447dec9f52139b2afde369cd51730f050bc94137556ae137f0509464219', + 'hex'), + 'SHA3-384': Buffer.from( + '01969a4db0888bc067a68a31fe5d0fc97e0b701f570565f7b25cb27707c6f020ff' + + '680f8553ec5c2d6885e9e91b39262ed1bde375525eb13fdf12089b7939c7689735' + + '0101c8b8d1129a217e8e956bef78cf7b9a0458523b04ac8e0b84ce73d54326f7a8' + + '704ee42fe183f3ef79d83e676f34dc5476e2342641a5b973d3d94e8503676fbbc5', + 'hex'), + 'SHA3-512': Buffer.from( + '000f362e914ee0136663cf57bf4085c25604af6dc198b4818751e1195ee7e41a16' + + '91be909dcbc2bae00b8917f6bb918eae3740ac1b76e0913137c2da1171d6400b55' + + '01ec6e1dc5987a27fe16fc2ce5c8e954088f898a9bbefb176eaa8bbd9ccc264c4c' + + 'cc38c83ac8b5a168f90228daf8405a2b9bf7829c263a646b4e1098e2ace38deec7', + 'hex') + } : {}) } } const curves = ['P-256', 'P-384', 'P-521']; - const hashes = ['SHA-1', 'SHA-256', 'SHA-384', 'SHA-512', 'SHA3-256', 'SHA3-384', 'SHA3-512']; + const hashes = ['SHA-1', 'SHA-256', 'SHA-384', 'SHA-512']; + + if (!process.features.openssl_is_boringssl) { + hashes.push('SHA3-256', 'SHA3-384', 'SHA3-512'); + } const vectors = []; curves.forEach((namedCurve) => { diff --git a/test/fixtures/crypto/hmac.js b/test/fixtures/crypto/hmac.js index 6505c6e2ae55b5..acdf3229a4bc7c 100644 --- a/test/fixtures/crypto/hmac.js +++ b/test/fixtures/crypto/hmac.js @@ -22,16 +22,18 @@ module.exports = function () { '5dcc359443aaf652fa1375d6b3e61fdcf29bb4a28bd5d3dcfa40f82f906bb280' + '0455db03b5d31fb972a15a6d0103a24e56d156a119c0e5a1e92a44c3c5657cf9', 'hex'), - 'SHA3-256': Buffer.from( - 'e588ec0811463d767241df1074b47ae4071b51f2ce36537ba69ccdc3fdc2b7a8', - 'hex'), - 'SHA3-384': Buffer.from( - '6b1da28eab1f582ad9718effe05e23d5fd2c9877a2d9443f90bec093bece2ea7' + - 'd2354cd0bdc5e147d2e9009373494488', 'hex'), - 'SHA3-512': Buffer.from( - '5dcc359443aaf652fa1375d6b3e61fdcf29bb4a28bd5d3dcfa40f82f906bb280' + - '0455db03b5d31fb972a15a6d0103a24e56d156a119c0e5a1e92a44c3c5657cf9', - 'hex') + ...(!process.features.openssl_is_boringssl ? { + 'SHA3-256': Buffer.from( + 'e588ec0811463d767241df1074b47ae4071b51f2ce36537ba69ccdc3fdc2b7a8', + 'hex'), + 'SHA3-384': Buffer.from( + '6b1da28eab1f582ad9718effe05e23d5fd2c9877a2d9443f90bec093bece2ea7' + + 'd2354cd0bdc5e147d2e9009373494488', 'hex'), + 'SHA3-512': Buffer.from( + '5dcc359443aaf652fa1375d6b3e61fdcf29bb4a28bd5d3dcfa40f82f906bb280' + + '0455db03b5d31fb972a15a6d0103a24e56d156a119c0e5a1e92a44c3c5657cf9', + 'hex') + } : {}) } const signatures = { @@ -46,16 +48,18 @@ module.exports = function () { '61fb278c3ffb0cce2bf1cf723ddfd8ef1f931c0c618c25907324605939e3f9a2' + 'c6f4af690bda3407dc2f5770f6a0a44b954d64a332e3ee0821abf82b7f3e99c1', 'hex'), - 'SHA3-256': Buffer.from( - 'c1ac5e11fcd50c48bf567f6e296632f5801c4eb07a8a47579b41dee971a3099b', - 'hex'), - 'SHA3-384': Buffer.from( - 'ac8c97f6dd8d9e16101063077c16b23fe291a5e6d149653e9ac7002365159317' + - 'adcfad511996578b0053a5c14b75f16c', 'hex'), - 'SHA3-512': Buffer.from( - '2162c2a8907e6b2f68599a69e81a464d8f076b5eeb555d98b4d20330034df3c7' + - 'cf35b1fa958a074ca12f0d242df39f0da3d4f1dbfb3629057798fe1f883974ee', - 'hex') + ...(!process.features.openssl_is_boringssl ? { + 'SHA3-256': Buffer.from( + 'c1ac5e11fcd50c48bf567f6e296632f5801c4eb07a8a47579b41dee971a3099b', + 'hex'), + 'SHA3-384': Buffer.from( + 'ac8c97f6dd8d9e16101063077c16b23fe291a5e6d149653e9ac7002365159317' + + 'adcfad511996578b0053a5c14b75f16c', 'hex'), + 'SHA3-512': Buffer.from( + '2162c2a8907e6b2f68599a69e81a464d8f076b5eeb555d98b4d20330034df3c7' + + 'cf35b1fa958a074ca12f0d242df39f0da3d4f1dbfb3629057798fe1f883974ee', + 'hex') + } : {}) } const vectors = []; diff --git a/test/fixtures/crypto/rsa_pkcs.js b/test/fixtures/crypto/rsa_pkcs.js index 4630e4af913580..d54c44b6d820d8 100644 --- a/test/fixtures/crypto/rsa_pkcs.js +++ b/test/fixtures/crypto/rsa_pkcs.js @@ -97,33 +97,35 @@ module.exports = function () { '7a6335c70e193235dcda48add6858626bd96311e60f7e5ea4491b6c1e6248afe12b' + 'bbd54f8869b043a5b0444562813f0a98b300356f306e6b783a29f3bec97ca40ea20' + '062cab8926ec5d96aa387cc84821a6d72b8ea126e7d', 'hex'), - 'sha3-256': Buffer.from( - 'be1b476c1911a01d71710fd8a2f3158d6f7839e91443b01bed30dfdd04336d80c6b' + - 'f692c06fad254877901c10a73853e8fb202a29cddefdf16c3adcda1fc123625897d' + - '1b81b32a9dec38957e023be221d8f31e7470ad32e761edce9170eefa37ec19bd0c3' + - 'e0b0ad2a244e98f54a08f873efb63c6fad14d7322b50eb05b6bae767305da92a90a' + - '53cdae52b0d81e158a00003ec626e50423b7377a34a7b28cc7483b55bfde05bd431' + - 'cfa436c38c285531e0d476ee13f151c8ae832ffd51ba00f2ab06f1844e73c0fe0f6' + - 'ce17d966b1e07727af4161368aa0a74a594a6fdb782b46a9ae6098799c366fc0d71' + - '1b2d965cf5eeeed9175b39b1d0bcefdd7df376e8ac9', 'hex'), - 'sha3-384': Buffer.from( - '002eaf5837443f1a33dc03729a308c503888d7a8cc013be424a91bce18105f7334a' + - '499a5eddc5f4fab2fdf80f52988d53bf8bd5e78c3ce1a43abaf3b8146c260b6ce8b' + - 'ffc9857f4b35c190cea85921c46d3ab573113744472d1afb637a0e9ab5021bcb355' + - '7f5b52faf89fa864a7d3bf5799096c54ee53fa139e1bc13842a2a5bf0f1d85f041d' + - 'a4e0e87425b421f22f0240ad62ef77ba6f090e0d48e17c07fd1e477c7e16a3196f5' + - '0142d0f0c5e525a10325569e5a1f50cb4577e782a643972857cc918ae5409587d9e' + - '44e1c1e89540e87deed7dda5005ac63ba609f522fdd92c81d95c1ffa383558a10f3' + - '064f59ca0534bfad31acbf3e2807cb7d3147c59ee4d', 'hex'), - 'sha3-512': Buffer.from( - '561585b621c916453762285c8bb6ede3f303074ad6f2826ca15b3900e49c4d94c07' + - 'aab0b875eaa79049ba2ed97e9a87c44fff9bffe638a1bf8c4db69c627b6adbe8fca' + - '2b38cb8b4c2810a16286bef498327b9db4b53043ed5012c7c58f037edf669baf772' + - '9b58e413e133ebb90a5fcb6dc3936f4f87971c0e85f362189b4279bbb2d9293a427' + - '5653068c1bc8772cebc4733a5d1df0b454d4f628c645c22bb1c8cc601fbc92dc091' + - 'db38fad4a36289ae9ed424c46643a8161a102ae511877d25f2eab7342dff6b92bf3' + - '65951e76ee84c2bd84a595f63d7cc04d00e1589870956491e518b3ba245efc37a28' + - 'ec018d8788a92ab93a90bb314f9ab0788a0b5b50489', 'hex') + ...(!process.features.openssl_is_boringssl ? { + 'sha3-256': Buffer.from( + 'be1b476c1911a01d71710fd8a2f3158d6f7839e91443b01bed30dfdd04336d80c6b' + + 'f692c06fad254877901c10a73853e8fb202a29cddefdf16c3adcda1fc123625897d' + + '1b81b32a9dec38957e023be221d8f31e7470ad32e761edce9170eefa37ec19bd0c3' + + 'e0b0ad2a244e98f54a08f873efb63c6fad14d7322b50eb05b6bae767305da92a90a' + + '53cdae52b0d81e158a00003ec626e50423b7377a34a7b28cc7483b55bfde05bd431' + + 'cfa436c38c285531e0d476ee13f151c8ae832ffd51ba00f2ab06f1844e73c0fe0f6' + + 'ce17d966b1e07727af4161368aa0a74a594a6fdb782b46a9ae6098799c366fc0d71' + + '1b2d965cf5eeeed9175b39b1d0bcefdd7df376e8ac9', 'hex'), + 'sha3-384': Buffer.from( + '002eaf5837443f1a33dc03729a308c503888d7a8cc013be424a91bce18105f7334a' + + '499a5eddc5f4fab2fdf80f52988d53bf8bd5e78c3ce1a43abaf3b8146c260b6ce8b' + + 'ffc9857f4b35c190cea85921c46d3ab573113744472d1afb637a0e9ab5021bcb355' + + '7f5b52faf89fa864a7d3bf5799096c54ee53fa139e1bc13842a2a5bf0f1d85f041d' + + 'a4e0e87425b421f22f0240ad62ef77ba6f090e0d48e17c07fd1e477c7e16a3196f5' + + '0142d0f0c5e525a10325569e5a1f50cb4577e782a643972857cc918ae5409587d9e' + + '44e1c1e89540e87deed7dda5005ac63ba609f522fdd92c81d95c1ffa383558a10f3' + + '064f59ca0534bfad31acbf3e2807cb7d3147c59ee4d', 'hex'), + 'sha3-512': Buffer.from( + '561585b621c916453762285c8bb6ede3f303074ad6f2826ca15b3900e49c4d94c07' + + 'aab0b875eaa79049ba2ed97e9a87c44fff9bffe638a1bf8c4db69c627b6adbe8fca' + + '2b38cb8b4c2810a16286bef498327b9db4b53043ed5012c7c58f037edf669baf772' + + '9b58e413e133ebb90a5fcb6dc3936f4f87971c0e85f362189b4279bbb2d9293a427' + + '5653068c1bc8772cebc4733a5d1df0b454d4f628c645c22bb1c8cc601fbc92dc091' + + 'db38fad4a36289ae9ed424c46643a8161a102ae511877d25f2eab7342dff6b92bf3' + + '65951e76ee84c2bd84a595f63d7cc04d00e1589870956491e518b3ba245efc37a28' + + 'ec018d8788a92ab93a90bb314f9ab0788a0b5b50489', 'hex') + } : {}) } const vectors = [ @@ -159,30 +161,32 @@ module.exports = function () { plaintext, signature: signatures['sha-512'] }, - { - publicKeyBuffer: spki, - privateKeyBuffer: pkcs8, - algorithm: { name: 'RSASSA-PKCS1-v1_5' }, - hash: 'SHA3-256', - plaintext, - signature: signatures['sha3-256'] - }, - { - publicKeyBuffer: spki, - privateKeyBuffer: pkcs8, - algorithm: { name: 'RSASSA-PKCS1-v1_5' }, - hash: 'SHA3-384', - plaintext, - signature: signatures['sha3-384'] - }, - { - publicKeyBuffer: spki, - privateKeyBuffer: pkcs8, - algorithm: { name: 'RSASSA-PKCS1-v1_5' }, - hash: 'SHA3-512', - plaintext, - signature: signatures['sha3-512'] - }, + ...(!process.features.openssl_is_boringssl ? [ + { + publicKeyBuffer: spki, + privateKeyBuffer: pkcs8, + algorithm: { name: 'RSASSA-PKCS1-v1_5' }, + hash: 'SHA3-256', + plaintext, + signature: signatures['sha3-256'] + }, + { + publicKeyBuffer: spki, + privateKeyBuffer: pkcs8, + algorithm: { name: 'RSASSA-PKCS1-v1_5' }, + hash: 'SHA3-384', + plaintext, + signature: signatures['sha3-384'] + }, + { + publicKeyBuffer: spki, + privateKeyBuffer: pkcs8, + algorithm: { name: 'RSASSA-PKCS1-v1_5' }, + hash: 'SHA3-512', + plaintext, + signature: signatures['sha3-512'] + }, + ] : []), ]; return vectors; diff --git a/test/fixtures/crypto/rsa_pss.js b/test/fixtures/crypto/rsa_pss.js index 101122b2ffe31c..423f2c4d77bfc9 100644 --- a/test/fixtures/crypto/rsa_pss.js +++ b/test/fixtures/crypto/rsa_pss.js @@ -150,42 +150,44 @@ module.exports = function() { 'b68c04bfe452c3adc6c10066a915231b7b404727eb6201b4921eb96d9407de2b963' + '3879ceb71d759d9828d7b4d062f6ef100757d8328187caf57dfb859d1555345207c' + '1cce7905c3564c08fec78867a53d5a2cf84810e1ffa', 'hex'), - 'sha3-512, no salt': Buffer.from( - 'd2430dc87abeaa7d13f7cec8510f1a296e1c608f44b1696829c59a99e8eefe9b2ee' + - '6ee8ad6fdc93c24fcba2f04d1da195924b6209717e1992c10ed9f4783478765fe34' + - '3e761203bff9d326bb6dc2061b0a7554c8ce0814b29249136c20c8e30054df0c6bc' + - '656509a82845149368896690e32ff5dd32ef01543686f01d6a69bb438b049e66a8b' + - 'df485a13edcd7dc482da4cc57d0b740aca3e56f0da247794e600afd27b22b6da13b' + - 'cc15dd2059b525f8cb6bcd07540aa843f0ae51d4b0eea27045485914b908bdd01d0' + - 'a9d42379f9f7180f4ad162ff73df5fed0200eb02ad01473975d54a77c15a9c61a3c' + - 'b5e27de5d1eecc363d45506f7123a5ddd115c5e4c9e', 'hex'), - 'sha3-256, salted': Buffer.from( - '59cb9cce6ae838eb20d38d6af4acb9b866b0753bb7df9e441037d788512c03279e8' + - '3d9a9cf5c0921fe1c0b6e8e895a8c0ad24a18b123f809b34ef2a3a1f05974030320' + - '435692ef5d378cef4368c3658c098a25371dfaf1c0b6910f653a4ec15f2c08956c1' + - '405136c2aba7f25a808fa7dbf57a4cb2978bd91af710b27ee239d955c8cac7a76ae' + - '9085cefeda2a585a99cc948f064b5da66a9c4aa4f3f767ac905a9f314b47038e05c' + - '3608fbb7e67a278e4f009a62c3cd3fdf43692e759d9361be1217999a76a69d4d119' + - 'f8791a90e207e46b3f6125721f56fd819292d06a3cdae2c62c9a1dc0d964a06036c' + - '8c18661cc6c873532a3536ab51e1ce210926db299e2', 'hex'), - 'sha3-384, salted': Buffer.from( - '8d1f9297c8169f27f0c58827dba991d862de58c1155f612ad2995d2bf862d051c4a' + - '91b48571849b0412384382e5b77990de6a3c84010046b35c4a504f175a3479483d9' + - '5c58f86bb96d53a27e59d6f67fddaae295ce90610f5086acc711557c2c85aac32d3' + - '24199cff2367ae44e1d91307a98c8cbfb085a8bce6b1c20714711bc15b0eddb7881' + - '823227d4be477ffdad8093663a6a1fc62eb39c49c2c3a821c2b202cf7904b49ca92' + - '3c83819602bb13931577354a80f99309030044935b1cd41f0513160e661db1959fb' + - '1ec15f087f3d288e875d54cbf070ec860b0aeecc951ea65e97cd5460750d4b7de52' + - '22cb9e7466b1f506ecf6a81fc399dfd8334160f9084', 'hex'), - 'sha3-512, salted': Buffer.from( - '7b6d7be418c5d37cc8070698b8b03d818ecd8b673d047d34921913f6d59c69cb496' + - '172d6118207d9ff92b8e1246acf0e03a845d935a70f8a82c3d5d6db6a1a0e337269' + - '4b904372413dcbaa7ac5486bc8ccaf70d7e9470be82b928a90017e272cf9761ed26' + - 'c160fe874a2675a4fb2acad72c50fbfffdd70b5a6f2919553d7ea1829934670f8de' + - 'f2a5c2816404b1aa153323c92c58400622f184b9b0463fa48d6b27091f68c287e3f' + - '6d9ab9eb451711a5d984c547f3d56f14a686a89ddf36c47ce25092b8c6530904de9' + - '5df7fc602fe9394315f1b1847aae304cb5ad71e2cb78acfbc997a87a9d62a6898bb' + - '6d84a81bb89b50186265f4be171a93d837a4bf777c8', 'hex') + ...(!process.features.openssl_is_boringssl ? { + 'sha3-512, no salt': Buffer.from( + 'd2430dc87abeaa7d13f7cec8510f1a296e1c608f44b1696829c59a99e8eefe9b2ee' + + '6ee8ad6fdc93c24fcba2f04d1da195924b6209717e1992c10ed9f4783478765fe34' + + '3e761203bff9d326bb6dc2061b0a7554c8ce0814b29249136c20c8e30054df0c6bc' + + '656509a82845149368896690e32ff5dd32ef01543686f01d6a69bb438b049e66a8b' + + 'df485a13edcd7dc482da4cc57d0b740aca3e56f0da247794e600afd27b22b6da13b' + + 'cc15dd2059b525f8cb6bcd07540aa843f0ae51d4b0eea27045485914b908bdd01d0' + + 'a9d42379f9f7180f4ad162ff73df5fed0200eb02ad01473975d54a77c15a9c61a3c' + + 'b5e27de5d1eecc363d45506f7123a5ddd115c5e4c9e', 'hex'), + 'sha3-256, salted': Buffer.from( + '59cb9cce6ae838eb20d38d6af4acb9b866b0753bb7df9e441037d788512c03279e8' + + '3d9a9cf5c0921fe1c0b6e8e895a8c0ad24a18b123f809b34ef2a3a1f05974030320' + + '435692ef5d378cef4368c3658c098a25371dfaf1c0b6910f653a4ec15f2c08956c1' + + '405136c2aba7f25a808fa7dbf57a4cb2978bd91af710b27ee239d955c8cac7a76ae' + + '9085cefeda2a585a99cc948f064b5da66a9c4aa4f3f767ac905a9f314b47038e05c' + + '3608fbb7e67a278e4f009a62c3cd3fdf43692e759d9361be1217999a76a69d4d119' + + 'f8791a90e207e46b3f6125721f56fd819292d06a3cdae2c62c9a1dc0d964a06036c' + + '8c18661cc6c873532a3536ab51e1ce210926db299e2', 'hex'), + 'sha3-384, salted': Buffer.from( + '8d1f9297c8169f27f0c58827dba991d862de58c1155f612ad2995d2bf862d051c4a' + + '91b48571849b0412384382e5b77990de6a3c84010046b35c4a504f175a3479483d9' + + '5c58f86bb96d53a27e59d6f67fddaae295ce90610f5086acc711557c2c85aac32d3' + + '24199cff2367ae44e1d91307a98c8cbfb085a8bce6b1c20714711bc15b0eddb7881' + + '823227d4be477ffdad8093663a6a1fc62eb39c49c2c3a821c2b202cf7904b49ca92' + + '3c83819602bb13931577354a80f99309030044935b1cd41f0513160e661db1959fb' + + '1ec15f087f3d288e875d54cbf070ec860b0aeecc951ea65e97cd5460750d4b7de52' + + '22cb9e7466b1f506ecf6a81fc399dfd8334160f9084', 'hex'), + 'sha3-512, salted': Buffer.from( + '7b6d7be418c5d37cc8070698b8b03d818ecd8b673d047d34921913f6d59c69cb496' + + '172d6118207d9ff92b8e1246acf0e03a845d935a70f8a82c3d5d6db6a1a0e337269' + + '4b904372413dcbaa7ac5486bc8ccaf70d7e9470be82b928a90017e272cf9761ed26' + + 'c160fe874a2675a4fb2acad72c50fbfffdd70b5a6f2919553d7ea1829934670f8de' + + 'f2a5c2816404b1aa153323c92c58400622f184b9b0463fa48d6b27091f68c287e3f' + + '6d9ab9eb451711a5d984c547f3d56f14a686a89ddf36c47ce25092b8c6530904de9' + + '5df7fc602fe9394315f1b1847aae304cb5ad71e2cb78acfbc997a87a9d62a6898bb' + + '6d84a81bb89b50186265f4be171a93d837a4bf777c8', 'hex') + } : {}) } const vectors = [ @@ -253,54 +255,56 @@ module.exports = function() { plaintext, signature: signatures['sha-512, salted'] }, - { - publicKeyBuffer: spki, - privateKeyBuffer: pkcs8, - algorithm: { name: 'RSA-PSS', saltLength: 0 }, - hash: 'SHA3-256', - plaintext, - signature: signatures['sha3-256, no salt'] - }, - { - publicKeyBuffer: spki, - privateKeyBuffer: pkcs8, - algorithm: { name: 'RSA-PSS', saltLength: 0 }, - hash: 'SHA3-384', - plaintext, - signature: signatures['sha3-384, no salt'] - }, - { - publicKeyBuffer: spki, - privateKeyBuffer: pkcs8, - algorithm: { name: 'RSA-PSS', saltLength: 0 }, - hash: 'SHA3-512', - plaintext, - signature: signatures['sha3-512, no salt'] - }, - { - publicKeyBuffer: spki, - privateKeyBuffer: pkcs8, - algorithm: { name: 'RSA-PSS', saltLength: 32 }, - hash: 'SHA3-256', - plaintext, - signature: signatures['sha3-256, salted'] - }, - { - publicKeyBuffer: spki, - privateKeyBuffer: pkcs8, - algorithm: { name: 'RSA-PSS', saltLength: 48 }, - hash: 'SHA3-384', - plaintext, - signature: signatures['sha3-384, salted'] - }, - { - publicKeyBuffer: spki, - privateKeyBuffer: pkcs8, - algorithm: { name: 'RSA-PSS', saltLength: 64 }, - hash: 'SHA3-512', - plaintext, - signature: signatures['sha3-512, salted'] - } + ...(!process.features.openssl_is_boringssl ? [ + { + publicKeyBuffer: spki, + privateKeyBuffer: pkcs8, + algorithm: { name: 'RSA-PSS', saltLength: 0 }, + hash: 'SHA3-256', + plaintext, + signature: signatures['sha3-256, no salt'] + }, + { + publicKeyBuffer: spki, + privateKeyBuffer: pkcs8, + algorithm: { name: 'RSA-PSS', saltLength: 0 }, + hash: 'SHA3-384', + plaintext, + signature: signatures['sha3-384, no salt'] + }, + { + publicKeyBuffer: spki, + privateKeyBuffer: pkcs8, + algorithm: { name: 'RSA-PSS', saltLength: 0 }, + hash: 'SHA3-512', + plaintext, + signature: signatures['sha3-512, no salt'] + }, + { + publicKeyBuffer: spki, + privateKeyBuffer: pkcs8, + algorithm: { name: 'RSA-PSS', saltLength: 32 }, + hash: 'SHA3-256', + plaintext, + signature: signatures['sha3-256, salted'] + }, + { + publicKeyBuffer: spki, + privateKeyBuffer: pkcs8, + algorithm: { name: 'RSA-PSS', saltLength: 48 }, + hash: 'SHA3-384', + plaintext, + signature: signatures['sha3-384, salted'] + }, + { + publicKeyBuffer: spki, + privateKeyBuffer: pkcs8, + algorithm: { name: 'RSA-PSS', saltLength: 64 }, + hash: 'SHA3-512', + plaintext, + signature: signatures['sha3-512, salted'] + } + ] : []), ]; return vectors; diff --git a/test/fixtures/kill-signal-for-watch.js b/test/fixtures/kill-signal-for-watch.js new file mode 100644 index 00000000000000..bfa67a63a65db0 --- /dev/null +++ b/test/fixtures/kill-signal-for-watch.js @@ -0,0 +1,4 @@ +process.on('SIGTERM', () => { console.log('__SIGTERM received__'); process.exit(); }); +process.on('SIGINT', () => { console.log('__SIGINT received__'); process.exit(); }); +process.send('script ready'); +setTimeout(() => {}, 100_000); diff --git a/test/fixtures/module-hooks/sync-and-async/app.js b/test/fixtures/module-hooks/sync-and-async/app.js new file mode 100644 index 00000000000000..c14477b70f7183 --- /dev/null +++ b/test/fixtures/module-hooks/sync-and-async/app.js @@ -0,0 +1,2 @@ +console.log('Hello world'); + diff --git a/test/fixtures/module-hooks/sync-and-async/async-customize-loader.js b/test/fixtures/module-hooks/sync-and-async/async-customize-loader.js new file mode 100644 index 00000000000000..ac961f0b97a3ab --- /dev/null +++ b/test/fixtures/module-hooks/sync-and-async/async-customize-loader.js @@ -0,0 +1,10 @@ +export async function load(url, context, nextLoad) { + if (url.endsWith('app.js')) { + return { + shortCircuit: true, + format: 'module', + source: 'console.log("customized by async hook");', + }; + } + return nextLoad(url, context); +} diff --git a/test/fixtures/module-hooks/sync-and-async/async-customize.js b/test/fixtures/module-hooks/sync-and-async/async-customize.js new file mode 100644 index 00000000000000..78bf7fc5564bd8 --- /dev/null +++ b/test/fixtures/module-hooks/sync-and-async/async-customize.js @@ -0,0 +1,3 @@ +import { register } from 'node:module'; + +register(new URL('async-customize-loader.js', import.meta.url)); diff --git a/test/fixtures/module-hooks/sync-and-async/async-forward-loader.js b/test/fixtures/module-hooks/sync-and-async/async-forward-loader.js new file mode 100644 index 00000000000000..4a1ced7e8dbfbf --- /dev/null +++ b/test/fixtures/module-hooks/sync-and-async/async-forward-loader.js @@ -0,0 +1,3 @@ +export async function load(url, context, nextLoad) { + return nextLoad(url, context); +} diff --git a/test/fixtures/module-hooks/sync-and-async/async-forward.js b/test/fixtures/module-hooks/sync-and-async/async-forward.js new file mode 100644 index 00000000000000..fd10aa6df8987c --- /dev/null +++ b/test/fixtures/module-hooks/sync-and-async/async-forward.js @@ -0,0 +1,3 @@ +import { register } from 'node:module'; + +register(new URL('async-forward-loader.js', import.meta.url)); diff --git a/test/fixtures/module-hooks/sync-and-async/sync-customize.js b/test/fixtures/module-hooks/sync-and-async/sync-customize.js new file mode 100644 index 00000000000000..90d63db1c7cc2e --- /dev/null +++ b/test/fixtures/module-hooks/sync-and-async/sync-customize.js @@ -0,0 +1,14 @@ +import { registerHooks } from 'node:module'; + +registerHooks({ + load(url, context, nextLoad) { + if (url.endsWith('app.js')) { + return { + shortCircuit: true, + format: 'module', + source: 'console.log("customized by sync hook")', + }; + } + return nextLoad(url, context); + }, +}); diff --git a/test/fixtures/module-hooks/sync-and-async/sync-forward.js b/test/fixtures/module-hooks/sync-and-async/sync-forward.js new file mode 100644 index 00000000000000..2688a240dc88f8 --- /dev/null +++ b/test/fixtures/module-hooks/sync-and-async/sync-forward.js @@ -0,0 +1,7 @@ +import { registerHooks } from 'node:module'; + +registerHooks({ + load(url, context, nextLoad) { + return nextLoad(url, context); + }, +}); diff --git a/test/module-hooks/test-module-hooks-load-async-and-sync.js b/test/module-hooks/test-module-hooks-load-async-and-sync.js new file mode 100644 index 00000000000000..75f4987942f93d --- /dev/null +++ b/test/module-hooks/test-module-hooks-load-async-and-sync.js @@ -0,0 +1,32 @@ +'use strict'; +// This tests that sync and async hooks can be mixed. + +require('../common'); +const { spawnSyncAndAssert } = require('../common/child_process'); +const fixtures = require('../common/fixtures'); + +const app = fixtures.path('module-hooks', 'sync-and-async', 'app.js'); + +const testCases = [ + // When mixing sync and async hooks, the sync ones always run first. + { preload: ['sync-customize', 'async-customize'], stdout: 'customized by sync hook' }, + { preload: ['async-customize', 'sync-customize'], stdout: 'customized by sync hook' }, + // It should still work when neither hook does any customization. + { preload: ['sync-forward', 'async-forward'], stdout: 'Hello world' }, + { preload: ['async-forward', 'sync-forward'], stdout: 'Hello world' }, + // It should work when only one hook is customizing. + { preload: ['sync-customize', 'async-forward'], stdout: 'customized by sync hook' }, + { preload: ['async-customize', 'sync-forward'], stdout: 'customized by async hook' }, +]; + + +for (const { preload, stdout } of testCases) { + const importArgs = []; + for (const p of preload) { + importArgs.push('--import', fixtures.fileURL(`module-hooks/sync-and-async/${p}.js`)); + } + spawnSyncAndAssert(process.execPath, [...importArgs, app], { + stdout, + trim: true, + }); +} diff --git a/test/parallel/parallel.status b/test/parallel/parallel.status index 5cb15393e42d4f..bf040bf2ee9c72 100644 --- a/test/parallel/parallel.status +++ b/test/parallel/parallel.status @@ -60,6 +60,8 @@ test-http-server-headers-timeout-keepalive: PASS,FLAKY test-http-server-request-timeout-keepalive: PASS,FLAKY # https://github.com/nodejs/node/issues/54534 test-runner-run-watch: PASS, FLAKY +# https://github.com/nodejs/node/issues/60050 +test-cluster-dgram-1: SKIP [$arch==arm || $arch==arm64] # https://github.com/nodejs/node/pull/31178 diff --git a/test/parallel/test-bootstrap-modules.js b/test/parallel/test-bootstrap-modules.js index a66a3ec9ce360f..b050f5bffde04a 100644 --- a/test/parallel/test-bootstrap-modules.js +++ b/test/parallel/test-bootstrap-modules.js @@ -107,15 +107,15 @@ expected.beforePreExec = new Set([ 'NativeModule internal/modules/typescript', 'NativeModule internal/data_url', 'NativeModule internal/mime', -]); - -expected.atRunTime = new Set([ + 'NativeModule internal/modules/esm/utils', 'Internal Binding worker', 'NativeModule internal/modules/run_main', 'NativeModule internal/net', 'NativeModule internal/dns/utils', +]); + +expected.atRunTime = new Set([ 'NativeModule internal/process/pre_execution', - 'NativeModule internal/modules/esm/utils', ]); const { isMainThread } = require('worker_threads'); @@ -173,7 +173,7 @@ if (common.hasIntl) { if (process.features.inspector) { expected.beforePreExec.add('Internal Binding inspector'); expected.beforePreExec.add('NativeModule internal/util/inspector'); - expected.atRunTime.add('NativeModule internal/inspector_async_hook'); + expected.beforePreExec.add('NativeModule internal/inspector_async_hook'); } // This is loaded if the test is run with NODE_V8_COVERAGE. diff --git a/test/parallel/test-buffer-alloc-unsafe-is-initialized-with-zero-fill-flag.js b/test/parallel/test-buffer-alloc-unsafe-is-initialized-with-zero-fill-flag.js new file mode 100644 index 00000000000000..075fa94e418457 --- /dev/null +++ b/test/parallel/test-buffer-alloc-unsafe-is-initialized-with-zero-fill-flag.js @@ -0,0 +1,21 @@ +// Flags: --expose-internals --zero-fill-buffers +// Verifies that Buffer.allocUnsafe() allocates initialized memory under --zero-fill-buffers by +// checking the usage count of the relevant native allocator code path. +'use strict'; + +const common = require('../common'); +if (!common.isDebug) { + common.skip('Only works in debug mode'); +} +const { internalBinding } = require('internal/test/binding'); +const { getGenericUsageCount } = internalBinding('debug'); +const assert = require('assert'); + +const initialUninitializedCount = getGenericUsageCount('NodeArrayBufferAllocator.Allocate.Uninitialized'); +const initialZeroFilledCount = getGenericUsageCount('NodeArrayBufferAllocator.Allocate.ZeroFilled'); +const buffer = Buffer.allocUnsafe(Buffer.poolSize + 1); +assert(buffer.every((b) => b === 0)); +const newUninitializedCount = getGenericUsageCount('NodeArrayBufferAllocator.Allocate.Uninitialized'); +const newZeroFilledCount = getGenericUsageCount('NodeArrayBufferAllocator.Allocate.ZeroFilled'); +assert.strictEqual(newUninitializedCount, initialUninitializedCount); +assert.notStrictEqual(newZeroFilledCount, initialZeroFilledCount); diff --git a/test/parallel/test-buffer-alloc-unsafe-is-uninitialized.js b/test/parallel/test-buffer-alloc-unsafe-is-uninitialized.js new file mode 100644 index 00000000000000..557851c8a7c231 --- /dev/null +++ b/test/parallel/test-buffer-alloc-unsafe-is-uninitialized.js @@ -0,0 +1,23 @@ +// Flags: --expose-internals +// Verifies that Buffer.allocUnsafe() indeed allocates uninitialized memory by checking +// the usage count of the relevant native allocator code path. +'use strict'; + +const common = require('../common'); +if (!common.isDebug) { + common.skip('Only works in debug mode'); +} +const { internalBinding } = require('internal/test/binding'); +const { getGenericUsageCount } = internalBinding('debug'); +const assert = require('assert'); + +const initialUninitializedCount = getGenericUsageCount('NodeArrayBufferAllocator.Allocate.Uninitialized'); +const initialZeroFilledCount = getGenericUsageCount('NodeArrayBufferAllocator.Allocate.ZeroFilled'); +Buffer.allocUnsafe(Buffer.poolSize + 1); +// We can't reliably check the contents of the buffer here because the OS or memory allocator +// used might zero-fill memory for us, or they might happen to return reused memory that was +// previously used by a process that zero-filled it. So instead we just check the usage counts. +const newUninitializedCount = getGenericUsageCount('NodeArrayBufferAllocator.Allocate.Uninitialized'); +const newZeroFilledCount = getGenericUsageCount('NodeArrayBufferAllocator.Allocate.ZeroFilled'); +assert.notStrictEqual(newUninitializedCount, initialUninitializedCount); +assert.strictEqual(newZeroFilledCount, initialZeroFilledCount); diff --git a/test/parallel/test-http-server-optimize-empty-requests.js b/test/parallel/test-http-server-optimize-empty-requests.js new file mode 100644 index 00000000000000..b88b8eb509d4db --- /dev/null +++ b/test/parallel/test-http-server-optimize-empty-requests.js @@ -0,0 +1,77 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); +const net = require('net'); + +let reqs = 0; +let optimizedReqs = 0; +const server = http.createServer({ + optimizeEmptyRequests: true +}, (req, res) => { + reqs++; + if (req._dumped) { + optimizedReqs++; + req.on('data', common.mustNotCall()); + req.on('end', common.mustNotCall()); + + assert.strictEqual(req._dumped, true); + assert.strictEqual(req.readableEnded, true); + assert.strictEqual(req.destroyed, true); + } + res.writeHead(200); + res.end('ok'); +}); + +server.listen(0, common.mustCall(async () => { + // GET request without Content-Length (should be optimized) + const getRequest = 'GET / HTTP/1.1\r\nHost: localhost\r\n\r\n'; + await makeRequest(getRequest); + + // HEAD request (should always be optimized regardless of headers) + const headRequest = 'HEAD / HTTP/1.1\r\nHost: localhost\r\n\r\n'; + await makeRequest(headRequest); + + // POST request without body headers (should be optimized) + const postWithoutBodyHeaders = 'POST / HTTP/1.1\r\nHost: localhost\r\n\r\n'; + await makeRequest(postWithoutBodyHeaders); + + // DELETE request without body headers (should be optimized) + const deleteWithoutBodyHeaders = 'DELETE / HTTP/1.1\r\nHost: localhost\r\n\r\n'; + await makeRequest(deleteWithoutBodyHeaders); + + // POST request with Content-Length header (should not be optimized) + const postWithContentLength = 'POST / HTTP/1.1\r\nHost: localhost\r\nContent-Length: 0\r\n\r\n'; + await makeRequest(postWithContentLength); + + // GET request with Content-Length header (should not be optimized) + const getWithContentLength = 'GET / HTTP/1.1\r\nHost: localhost\r\nContent-Length: 0\r\n\r\n'; + await makeRequest(getWithContentLength); + + // POST request with Transfer-Encoding header (should not be optimized) + const postWithTransferEncoding = 'POST / HTTP/1.1\r\nHost: localhost\r\nTransfer-Encoding: chunked\r\n\r\n'; + await makeRequest(postWithTransferEncoding); + + // GET request with Transfer-Encoding header (should not be optimized) + const getWithTransferEncoding = 'GET / HTTP/1.1\r\nHost: localhost\r\nTransfer-Encoding: chunked\r\n\r\n'; + await makeRequest(getWithTransferEncoding); + + server.close(); + + assert.strictEqual(reqs, 8, `Expected 8 requests but got ${reqs}`); + assert.strictEqual(optimizedReqs, 4, `Expected 4 optimized requests but got ${optimizedReqs}`); +})); + +function makeRequest(str) { + return new Promise((resolve) => { + const client = net.connect({ port: server.address().port }, common.mustCall(() => { + client.on('data', () => {}); + client.on('end', common.mustCall(() => { + resolve(); + })); + client.write(str); + client.end(); + })); + }); +} diff --git a/test/parallel/test-inspector-network-websocket.js b/test/parallel/test-inspector-network-websocket.js index 8f0d4cb75dc674..f0fc6a8216b5c5 100644 --- a/test/parallel/test-inspector-network-websocket.js +++ b/test/parallel/test-inspector-network-websocket.js @@ -29,8 +29,15 @@ function findFrameInInitiator(regex, initiator) { async function test() { await session.post('Network.enable'); + + const CUSTOM_HEADER_NAME = 'X-Custom-Header'; + const CUSTOM_HEADER_VALUE = 'CustomHeaderValue'; + const server = new WebSocketServer({ responseError: true, + customHandleUpgradeHeaders: [ + `${CUSTOM_HEADER_NAME}: ${CUSTOM_HEADER_VALUE}`, + ] }); await server.start(); const url = `ws://127.0.0.1:${server.port}/`; @@ -49,6 +56,11 @@ async function test() { assert.strictEqual(message.params.requestId, requestId); assert.strictEqual(message.params.response.status, 101); assert.strictEqual(message.params.response.statusText, 'Switching Protocols'); + assert.strictEqual(message.params.response.headers.upgrade, 'websocket'); + assert.strictEqual(message.params.response.headers.connection, 'Upgrade'); + assert.ok(message.params.response.headers['sec-websocket-accept']); + assert.ok(message.params.response.headers['sec-websocket-accept'].length > 0); + assert.strictEqual(message.params.response.headers[CUSTOM_HEADER_NAME.toLowerCase()], CUSTOM_HEADER_VALUE); assert.strictEqual(typeof message.params.timestamp, 'number'); socket.close(); })); diff --git a/test/parallel/test-internal-module-wrap.js b/test/parallel/test-internal-module-wrap.js index ce4d5dae52412c..2b7bcf009d14f1 100644 --- a/test/parallel/test-internal-module-wrap.js +++ b/test/parallel/test-internal-module-wrap.js @@ -91,7 +91,7 @@ function testLinkMismatch() { }, { code: 'ERR_MODULE_LINK_MISMATCH', // Test that ModuleCacheKey::ToString() is used in the error message. - message: `Module request 'ModuleCacheKey("bar")' at index 0 must be linked to the same module requested at index 1` + message: `Module request 'ModuleCacheKey("bar")' at index 1 must be linked to the same module requested at index 0` }); } diff --git a/test/parallel/test-max-old-space-size-percentage.js b/test/parallel/test-max-old-space-size-percentage.js index d4da10b3012662..b84344ddb3988d 100644 --- a/test/parallel/test-max-old-space-size-percentage.js +++ b/test/parallel/test-max-old-space-size-percentage.js @@ -122,7 +122,11 @@ assert( ); // Validate heap sizes against system memory -const totalMemoryMB = Math.floor(os.totalmem() / 1024 / 1024); +// When pointer compression is enabled, the maximum total memory is 4 GB +const totalmem = Math.floor(os.totalmem() / 1024 / 1024); +const totalMemoryMB = process.config.variables.v8_enable_pointer_compression ? + Math.min(4096, totalmem) : + totalmem; const uint64Max = 2 ** 64 - 1; const constrainedMemory = process.constrainedMemory(); const constrainedMemoryMB = Math.floor(constrainedMemory / 1024 / 1024); diff --git a/test/parallel/test-permission-dc-worker-threads.js b/test/parallel/test-permission-dc-worker-threads.js deleted file mode 100644 index 4fdb566f9e1701..00000000000000 --- a/test/parallel/test-permission-dc-worker-threads.js +++ /dev/null @@ -1,19 +0,0 @@ -// Flags: --permission --allow-fs-read=* --experimental-test-module-mocks -'use strict'; - -const common = require('../common'); -const assert = require('node:assert'); - -{ - const diagnostics_channel = require('node:diagnostics_channel'); - diagnostics_channel.subscribe('worker_threads', common.mustNotCall()); - const { mock } = require('node:test'); - - // Module mocking should throw instead of posting to worker_threads dc - assert.throws(() => { - mock.module('node:path'); - }, common.expectsError({ - code: 'ERR_ACCESS_DENIED', - permission: 'WorkerThreads', - })); -} diff --git a/test/parallel/test-process-get-builtin.mjs b/test/parallel/test-process-get-builtin.mjs index 8131a470281c6a..de863ff3597641 100644 --- a/test/parallel/test-process-get-builtin.mjs +++ b/test/parallel/test-process-get-builtin.mjs @@ -1,4 +1,4 @@ -import { hasCrypto, hasIntl, hasSQLite } from '../common/index.mjs'; +import { hasCrypto, hasIntl, hasInspector, hasSQLite } from '../common/index.mjs'; import assert from 'node:assert'; import { builtinModules } from 'node:module'; import { isMainThread } from 'node:worker_threads'; @@ -39,6 +39,10 @@ if (!hasIntl) { // TODO(@jasnell): Remove this once node:quic graduates from unflagged. publicBuiltins.delete('node:quic'); +if (!hasInspector) { + publicBuiltins.delete('inspector'); + publicBuiltins.delete('inspector/promises'); +} if (!hasSQLite) { publicBuiltins.delete('node:sqlite'); } diff --git a/test/parallel/test-runner-module-mocking.js b/test/parallel/test-runner-module-mocking.js index 89e08a9e22a362..dcb6f84597fe71 100644 --- a/test/parallel/test-runner-module-mocking.js +++ b/test/parallel/test-runner-module-mocking.js @@ -679,44 +679,3 @@ test('wrong import syntax should throw error after module mocking', async () => assert.match(stderr, /Error \[ERR_MODULE_NOT_FOUND\]: Cannot find module/); assert.strictEqual(code, 1); }); - -test('should throw ERR_ACCESS_DENIED when permission model is enabled', async (t) => { - const cwd = fixtures.path('test-runner'); - const fixture = fixtures.path('test-runner', 'mock-nm.js'); - const args = [ - '--permission', - '--allow-fs-read=*', - '--experimental-test-module-mocks', - fixture, - ]; - const { - code, - stdout, - } = await common.spawnPromisified(process.execPath, args, { cwd }); - - assert.strictEqual(code, 1); - assert.match(stdout, /Error: Access to this API has been restricted/); - assert.match(stdout, /permission: 'WorkerThreads'/); -}); - -test('should work when --allow-worker is passed and permission model is enabled', async (t) => { - const cwd = fixtures.path('test-runner'); - const fixture = fixtures.path('test-runner', 'mock-nm.js'); - const args = [ - '--permission', - '--allow-fs-read=*', - '--allow-worker', - '--experimental-test-module-mocks', - fixture, - ]; - const { - code, - stdout, - stderr, - signal, - } = await common.spawnPromisified(process.execPath, args, { cwd }); - - assert.strictEqual(code, 0, stderr); - assert.strictEqual(signal, null); - assert.match(stdout, /pass 1/, stderr); -}); diff --git a/test/parallel/test-runner-output.mjs b/test/parallel/test-runner-output.mjs deleted file mode 100644 index f854447c4526b1..00000000000000 --- a/test/parallel/test-runner-output.mjs +++ /dev/null @@ -1,358 +0,0 @@ -// Flags: --expose-internals -import * as common from '../common/index.mjs'; -import * as fixtures from '../common/fixtures.mjs'; -import * as snapshot from '../common/assertSnapshot.js'; -import { describe, it } from 'node:test'; -import { hostname } from 'node:os'; -import { chdir, cwd } from 'node:process'; -import { fileURLToPath } from 'node:url'; - -const skipForceColors = - process.config.variables.icu_gyp_path !== 'tools/icu/icu-generic.gyp' || - process.config.variables.node_shared_openssl; - -// We're using dynamic import here to not break `NODE_REGENERATE_SNAPSHOTS`. -const canColorize = (await import('internal/tty')).default.getColorDepth() > 2; -const skipCoverageColors = !canColorize; - -function replaceTestDuration(str) { - return str - .replaceAll(/duration_ms: [0-9.]+/g, 'duration_ms: *') - .replaceAll(/duration_ms [0-9.]+/g, 'duration_ms *'); -} - -const root = fileURLToPath(new URL('../..', import.meta.url)).slice(0, -1); - -const color = '(\\[\\d+m)'; -const stackTraceBasePath = new RegExp(`${color}\\(${root.replaceAll(/[\\^$*+?.()|[\]{}]/g, '\\$&')}/?${color}(.*)${color}\\)`, 'g'); - -function replaceSpecDuration(str) { - return str - .replaceAll(/[0-9.]+ms/g, '*ms') - .replaceAll(/duration_ms [0-9.]+/g, 'duration_ms *') - .replace(stackTraceBasePath, '$3'); -} - -function replaceJunitDuration(str) { - return str - .replaceAll(/time="[0-9.]+"/g, 'time="*"') - .replaceAll(/duration_ms [0-9.]+/g, 'duration_ms *') - .replaceAll(`hostname="${hostname()}"`, 'hostname="HOSTNAME"') - .replaceAll(/file="[^"]*"/g, 'file="*"') - .replace(stackTraceBasePath, '$3'); -} - -function removeWindowsPathEscaping(str) { - return common.isWindows ? str.replaceAll(/\\\\/g, '\\') : str; -} - -function replaceTestLocationLine(str) { - return str.replaceAll(/(js:)(\d+)(:\d+)/g, '$1(LINE)$3'); -} - -// The Node test coverage returns results for all files called by the test. This -// will make the output file change if files like test/common/index.js change. -// This transform picks only the first line and then the lines from the test -// file. -function pickTestFileFromLcov(str) { - const lines = str.split(/\n/); - const firstLineOfTestFile = lines.findIndex( - (line) => line.startsWith('SF:') && line.trim().endsWith('output.js') - ); - const lastLineOfTestFile = lines.findIndex( - (line, index) => index > firstLineOfTestFile && line.trim() === 'end_of_record' - ); - return ( - lines[0] + '\n' + lines.slice(firstLineOfTestFile, lastLineOfTestFile + 1).join('\n') + '\n' - ); -} - -const defaultTransform = snapshot.transform( - snapshot.replaceWindowsLineEndings, - snapshot.replaceStackTrace, - removeWindowsPathEscaping, - snapshot.transformProjectRoot(), - snapshot.replaceWindowsPaths, - replaceTestDuration, - replaceTestLocationLine, -); -const specTransform = snapshot.transform( - replaceSpecDuration, - snapshot.replaceWindowsLineEndings, - snapshot.replaceStackTrace, - snapshot.replaceWindowsPaths, -); -const junitTransform = snapshot.transform( - replaceJunitDuration, - snapshot.replaceWindowsLineEndings, - snapshot.replaceStackTrace, - snapshot.replaceWindowsPaths, -); -const lcovTransform = snapshot.transform( - snapshot.replaceWindowsLineEndings, - snapshot.replaceStackTrace, - snapshot.transformProjectRoot(), - snapshot.replaceWindowsPaths, - pickTestFileFromLcov -); - - -const tests = [ - { name: 'test-runner/output/abort.js', flags: ['--test-reporter=tap'] }, - { - name: 'test-runner/output/abort-runs-after-hook.js', - flags: ['--test-reporter=tap'], - }, - { name: 'test-runner/output/abort_suite.js', flags: ['--test-reporter=tap'] }, - { name: 'test-runner/output/abort_hooks.js', flags: ['--test-reporter=tap'] }, - { name: 'test-runner/output/describe_it.js', flags: ['--test-reporter=tap'] }, - { - name: 'test-runner/output/describe_nested.js', - flags: ['--test-reporter=tap'], - }, - { name: 'test-runner/output/eval_dot.js', transform: specTransform }, - { name: 'test-runner/output/eval_spec.js', transform: specTransform }, - { name: 'test-runner/output/eval_tap.js' }, - { - name: 'test-runner/output/filtered-suite-delayed-build.js', - flags: ['--test-reporter=tap'], - }, - { - name: 'test-runner/output/filtered-suite-order.mjs', - flags: ['--test-reporter=tap'], - }, - { - name: 'test-runner/output/filtered-suite-throws.js', - flags: ['--test-reporter=tap'], - }, - { name: 'test-runner/output/hooks.js', flags: ['--test-reporter=tap'] }, - { name: 'test-runner/output/hooks_spec_reporter.js', transform: specTransform }, - { name: 'test-runner/output/skip-each-hooks.js', transform: specTransform }, - { name: 'test-runner/output/suite-skip-hooks.js', transform: specTransform }, - { - name: 'test-runner/output/timeout_in_before_each_should_not_affect_further_tests.js', - flags: ['--test-reporter=tap'], - }, - { - name: 'test-runner/output/test-timeout-flag.js', - flags: [ - '--test-reporter=tap', - '--test-timeout=100', - ], - }, - // --test-timeout should work with or without --test flag - { - name: 'test-runner/output/test-timeout-flag.js', - flags: [ - '--test-reporter=tap', - '--test-timeout=100', - '--test', - ], - }, - { - name: 'test-runner/output/hooks-with-no-global-test.js', - flags: ['--test-reporter=tap'], - }, - { - name: 'test-runner/output/global-hooks-with-no-tests.js', - flags: ['--test-reporter=tap'], - }, - { - name: 'test-runner/output/before-and-after-each-too-many-listeners.js', - flags: ['--test-reporter=tap'], - }, - { - name: 'test-runner/output/before-and-after-each-with-timeout-too-many-listeners.js', - flags: ['--test-reporter=tap'], - }, - { name: 'test-runner/output/force_exit.js', transform: specTransform }, - { - name: 'test-runner/output/global_after_should_fail_the_test.js', - flags: ['--test-reporter=tap'], - }, - { - name: 'test-runner/output/no_refs.js', - flags: ['--test-reporter=tap'], - }, - { - name: 'test-runner/output/no_tests.js', - flags: ['--test-reporter=tap'], - }, - { name: 'test-runner/output/only_tests.js', flags: ['--test-reporter=tap'] }, - { name: 'test-runner/output/dot_reporter.js', transform: specTransform }, - { name: 'test-runner/output/junit_reporter.js', transform: junitTransform }, - { name: 'test-runner/output/spec_reporter_successful.js', transform: specTransform }, - { name: 'test-runner/output/spec_reporter.js', transform: specTransform }, - { name: 'test-runner/output/spec_reporter_cli.js', transform: specTransform }, - { - name: 'test-runner/output/source_mapped_locations.mjs', - flags: ['--test-reporter=tap'], - }, - process.features.inspector ? - { - name: 'test-runner/output/lcov_reporter.js', - transform: lcovTransform - } : - false, - { name: 'test-runner/output/output.js', flags: ['--test-reporter=tap'] }, - { name: 'test-runner/output/output_cli.js' }, - { - name: 'test-runner/output/name_and_skip_patterns.js', - flags: ['--test-reporter=tap'], - }, - { - name: 'test-runner/output/name_pattern.js', - flags: ['--test-reporter=tap'], - }, - { - name: 'test-runner/output/name_pattern_with_only.js', - flags: ['--test-reporter=tap'], - }, - { - name: 'test-runner/output/skip_pattern.js', - flags: ['--test-reporter=tap'], - }, - { - name: 'test-runner/output/unfinished-suite-async-error.js', - flags: ['--test-reporter=tap'], - }, - { name: 'test-runner/output/default_output.js', transform: specTransform, tty: true }, - { - name: 'test-runner/output/arbitrary-output.js', - flags: ['--test-reporter=tap'], - }, - { - name: 'test-runner/output/non-tty-forced-color-output.js', - transform: specTransform, - }, - canColorize ? { - name: 'test-runner/output/assertion-color-tty.mjs', - flags: ['--test', '--stack-trace-limit=0'], - transform: specTransform, - tty: true, - } : false, - { - name: 'test-runner/output/async-test-scheduling.mjs', - flags: ['--test-reporter=tap'], - }, - !skipForceColors ? { - name: 'test-runner/output/arbitrary-output-colored.js', - transform: snapshot.transform(specTransform, replaceTestDuration), tty: true - } : false, - { name: 'test-runner/output/dot_output_custom_columns.js', transform: specTransform, tty: true }, - { - name: 'test-runner/output/tap_escape.js', - transform: snapshot.transform( - snapshot.replaceWindowsLineEndings, - replaceTestDuration, - ), - flags: ['--test-reporter=tap'], - }, - { - name: 'test-runner/output/test-runner-plan.js', - flags: ['--test-reporter=tap'], - }, - { - name: 'test-runner/output/test-runner-watch-spec.mjs', - transform: specTransform, - }, - { - name: 'test-runner/output/test-runner-plan-timeout.js', - flags: ['--test-reporter=tap', '--test-force-exit'], - }, - process.features.inspector ? { - name: 'test-runner/output/coverage_failure.js', - flags: ['--test-reporter=tap', '--test-coverage-exclude=!test/**'], - } : false, - { - name: 'test-runner/output/test-diagnostic-warning-without-test-only-flag.js', - flags: ['--test', '--test-reporter=tap'], - }, - process.features.inspector ? { - name: 'test-runner/output/coverage-width-40.mjs', - flags: ['--test-reporter=tap', '--test-coverage-exclude=!test/**'], - } : false, - process.features.inspector ? { - name: 'test-runner/output/coverage-width-80.mjs', - flags: ['--test-reporter=tap', '--test-coverage-exclude=!test/**'], - } : false, - process.features.inspector && !skipCoverageColors ? { - name: 'test-runner/output/coverage-width-80-color.mjs', - flags: ['--test-coverage-exclude=!test/**'], - transform: specTransform, - tty: true - } : false, - process.features.inspector ? { - name: 'test-runner/output/coverage-width-100.mjs', - flags: ['--test-reporter=tap', '--test-coverage-exclude=!test/**'], - } : false, - process.features.inspector ? { - name: 'test-runner/output/coverage-width-150.mjs', - flags: ['--test-reporter=tap', '--test-coverage-exclude=!test/**'], - } : false, - process.features.inspector ? { - name: 'test-runner/output/coverage-width-infinity.mjs', - flags: ['--test-reporter=tap', '--test-coverage-exclude=!test/**'], - } : false, - process.features.inspector ? { - name: 'test-runner/output/coverage-width-80-uncovered-lines.mjs', - flags: ['--test-reporter=tap', '--test-coverage-exclude=!test/**'], - } : false, - process.features.inspector ? { - name: 'test-runner/output/coverage-width-100-uncovered-lines.mjs', - flags: ['--test-reporter=tap', '--test-coverage-exclude=!test/**'], - } : false, - process.features.inspector && !skipCoverageColors ? { - name: 'test-runner/output/coverage-width-80-uncovered-lines-color.mjs', - flags: ['--test-coverage-exclude=!test/**'], - transform: specTransform, - tty: true - } : false, - process.features.inspector ? { - name: 'test-runner/output/coverage-width-150-uncovered-lines.mjs', - flags: ['--test-reporter=tap', '--test-coverage-exclude=!test/**'], - } : false, - process.features.inspector ? { - name: 'test-runner/output/coverage-width-infinity-uncovered-lines.mjs', - flags: ['--test-reporter=tap', '--test-coverage-exclude=!test/**'], - } : false, - process.features.inspector ? { - name: 'test-runner/output/coverage-short-filename.mjs', - flags: ['--test-reporter=tap', '--test-coverage-exclude=../output/**'], - cwd: fixtures.path('test-runner/coverage-snap'), - } : false, - process.features.inspector ? { - name: 'test-runner/output/typescript-coverage.mts', - flags: ['--disable-warning=ExperimentalWarning', - '--test-reporter=tap', - '--experimental-transform-types', - '--experimental-test-module-mocks', - '--experimental-test-coverage', - '--test-coverage-exclude=!test/**'] - } : false, - process.features.inspector ? { - name: 'test-runner/output/coverage-with-mock.mjs', - flags: ['--disable-warning=ExperimentalWarning', - '--test-reporter=tap', - '--experimental-transform-types', - '--experimental-test-module-mocks', - '--experimental-test-coverage', - '--test-coverage-exclude=!test/**'] - } : false, -] -.filter(Boolean) -.map(({ flags, name, tty, transform, cwd }) => ({ - name, - fn: common.mustCall(async () => { - await snapshot.spawnAndAssert(fixtures.path(name), transform ?? defaultTransform, { tty, flags, cwd }); - }), -})); - -if (cwd() !== root) { - chdir(root); -} -describe('test runner output', { concurrency: true }, () => { - for (const { name, fn } of tests) { - it(name, fn); - } -}); diff --git a/test/parallel/test-runner-watch-mode-kill-signal.mjs b/test/parallel/test-runner-watch-mode-kill-signal.mjs deleted file mode 100644 index d59f4522fadc7f..00000000000000 --- a/test/parallel/test-runner-watch-mode-kill-signal.mjs +++ /dev/null @@ -1,131 +0,0 @@ -import * as common from '../common/index.mjs'; -import { describe, it, beforeEach } from 'node:test'; -import assert from 'node:assert'; -import { spawn } from 'node:child_process'; -import { writeFileSync } from 'node:fs'; -import tmpdir from '../common/tmpdir.js'; - -if (common.isWindows) { - common.skip('no signals on Windows'); -} - -if (common.isIBMi) { - common.skip('IBMi does not support `fs.watch()`'); -} - -if (common.isAIX) { - common.skip('folder watch capability is limited in AIX.'); -} - -const indexContents = ` - process.on('SIGTERM', () => { console.log('__SIGTERM received__'); process.exit(); }); - process.on('SIGINT', () => { console.log('__SIGINT received__'); process.exit(); }); - process.send('script ready'); - setTimeout(() => {}, 100_000); -`; - -let indexPath = ''; - -function refresh() { - tmpdir.refresh(); - indexPath = tmpdir.resolve('index.js'); - writeFileSync(indexPath, indexContents); -} - -describe('test runner watch mode with --watch-kill-signal', () => { - beforeEach(refresh); - - it('defaults to SIGTERM', async () => { - const child = spawn( - process.execPath, - ['--watch', indexPath], - { - cwd: tmpdir.path, - stdio: ['pipe', 'pipe', 'pipe', 'ipc'], - } - ); - - let stdout = ''; - child.stdout.on('data', (data) => { - stdout += `${data}`; - if (/__(SIGINT|SIGTERM) received__/.test(stdout)) { - child.kill(); - } - }); - - child.on('message', (msg) => { - if (msg === 'script ready') { - writeFileSync(indexPath, indexContents); - } - }); - - await new Promise((resolve) => - child.on('exit', () => { - resolve(); - }) - ); - - assert.match(stdout, /__SIGTERM received__/); - assert.doesNotMatch(stdout, /__SIGINT received__/); - }); - - it('can be overridden (to SIGINT)', async () => { - const child = spawn( - process.execPath, - ['--watch', '--watch-kill-signal', 'SIGINT', indexPath], - { - cwd: tmpdir.path, - stdio: ['pipe', 'pipe', 'pipe', 'ipc'], - } - ); - - let stdout = ''; - child.stdout.on('data', (data) => { - stdout += `${data}`; - if (/__(SIGINT|SIGTERM) received__/.test(stdout)) { - child.kill(); - } - }); - - child.on('message', (msg) => { - if (msg === 'script ready') { - writeFileSync(indexPath, indexContents); - } - }); - - await new Promise((resolve) => - child.on('exit', () => { - resolve(); - }) - ); - - assert.match(stdout, /__SIGINT received__/); - assert.doesNotMatch(stdout, /__SIGTERM received__/); - }); - - it('errors if an invalid signal is provided', async () => { - const currentRun = Promise.withResolvers(); - const child = spawn( - process.execPath, - ['--watch', '--watch-kill-signal', 'invalid_signal', indexPath], - { - cwd: tmpdir.path, - } - ); - let stdout = ''; - - child.stderr.on('data', (data) => { - stdout += data.toString(); - currentRun.resolve(); - }); - - await currentRun.promise; - - assert.match( - stdout, - new RegExp( - /TypeError \[ERR_UNKNOWN_SIGNAL\]: Unknown signal: invalid_signal/ - ) - ); - }); -}); diff --git a/test/parallel/test-sqlite-config.js b/test/parallel/test-sqlite-config.js new file mode 100644 index 00000000000000..13916f7407ed48 --- /dev/null +++ b/test/parallel/test-sqlite-config.js @@ -0,0 +1,56 @@ +'use strict'; +const { skipIfSQLiteMissing } = require('../common/index.mjs'); +const { test } = require('node:test'); +const assert = require('node:assert'); +const { DatabaseSync } = require('node:sqlite'); +skipIfSQLiteMissing(); + +function checkDefensiveMode(db) { + function journalMode() { + return db.prepare('PRAGMA journal_mode').get().journal_mode; + } + + assert.strictEqual(journalMode(), 'memory'); + db.exec('PRAGMA journal_mode=OFF'); + + switch (journalMode()) { + case 'memory': return true; // journal_mode unchanged, defensive mode must be active + case 'off': return false; // journal_mode now 'off', so defensive mode not active + default: throw new Error('unexpected journal_mode'); + } +} + +test('by default, defensive mode is off', (t) => { + const db = new DatabaseSync(':memory:'); + t.assert.strictEqual(checkDefensiveMode(db), false); +}); + +test('when passing { defensive: true } as config, defensive mode is on', (t) => { + const db = new DatabaseSync(':memory:', { + defensive: true + }); + t.assert.strictEqual(checkDefensiveMode(db), true); +}); + +test('defensive mode on after calling db.enableDefensive(true)', (t) => { + const db = new DatabaseSync(':memory:'); + db.enableDefensive(true); + t.assert.strictEqual(checkDefensiveMode(db), true); +}); + +test('defensive mode should be off after calling db.enableDefensive(false)', (t) => { + const db = new DatabaseSync(':memory:', { + defensive: true + }); + db.enableDefensive(false); + t.assert.strictEqual(checkDefensiveMode(db), false); +}); + +test('throws if options.defensive is provided but is not a boolean', (t) => { + t.assert.throws(() => { + new DatabaseSync(':memory:', { defensive: 42 }); + }, { + code: 'ERR_INVALID_ARG_TYPE', + message: 'The "options.defensive" argument must be a boolean.', + }); +}); diff --git a/test/parallel/test-util-format.js b/test/parallel/test-util-format.js index ad77c7cafd38fb..dfcf62c378b84b 100644 --- a/test/parallel/test-util-format.js +++ b/test/parallel/test-util-format.js @@ -588,7 +588,7 @@ assert.strictEqual( assert.strictEqual( util.format(new SharedArrayBuffer(4)), - 'SharedArrayBuffer { [Uint8Contents]: <00 00 00 00>, byteLength: 4 }' + 'SharedArrayBuffer { [Uint8Contents]: <00 00 00 00>, [byteLength]: 4 }' ); assert.strictEqual( diff --git a/test/parallel/test-util-inspect.js b/test/parallel/test-util-inspect.js index 89b798f20cba6d..ce44c29c4e8219 100644 --- a/test/parallel/test-util-inspect.js +++ b/test/parallel/test-util-inspect.js @@ -172,56 +172,67 @@ assert.doesNotMatch( const dv = new DataView(ab, 1, 2); assert.strictEqual( util.inspect(ab, showHidden), - 'ArrayBuffer { [Uint8Contents]: <01 02 03 04>, byteLength: 4 }' + 'ArrayBuffer { [Uint8Contents]: <01 02 03 04>, [byteLength]: 4 }' ); assert.strictEqual(util.inspect(new DataView(ab, 1, 2), showHidden), 'DataView {\n' + - ' byteLength: 2,\n' + - ' byteOffset: 1,\n' + - ' buffer: ArrayBuffer {' + - ' [Uint8Contents]: <01 02 03 04>, byteLength: 4 }\n}'); + ' [byteLength]: 2,\n' + + ' [byteOffset]: 1,\n' + + ' [buffer]: ArrayBuffer {' + + ' [Uint8Contents]: <01 02 03 04>, [byteLength]: 4 }\n}'); assert.strictEqual( util.inspect(ab, showHidden), - 'ArrayBuffer { [Uint8Contents]: <01 02 03 04>, byteLength: 4 }' + 'ArrayBuffer { [Uint8Contents]: <01 02 03 04>, [byteLength]: 4 }' ); assert.strictEqual(util.inspect(dv, showHidden), 'DataView {\n' + - ' byteLength: 2,\n' + - ' byteOffset: 1,\n' + - ' buffer: ArrayBuffer { [Uint8Contents]: ' + - '<01 02 03 04>, byteLength: 4 }\n}'); + ' [byteLength]: 2,\n' + + ' [byteOffset]: 1,\n' + + ' [buffer]: ArrayBuffer { [Uint8Contents]: ' + + '<01 02 03 04>, [byteLength]: 4 }\n}'); ab.x = 42; dv.y = 1337; assert.strictEqual(util.inspect(ab, showHidden), 'ArrayBuffer { [Uint8Contents]: <01 02 03 04>, ' + - 'byteLength: 4, x: 42 }'); - assert.strictEqual(util.inspect(dv, showHidden), + '[byteLength]: 4, x: 42 }'); + assert.strictEqual(util.inspect(dv, { showHidden, breakLength: 82 }), 'DataView {\n' + - ' byteLength: 2,\n' + - ' byteOffset: 1,\n' + - ' buffer: ArrayBuffer { [Uint8Contents]: <01 02 03 04>,' + - ' byteLength: 4, x: 42 },\n' + + ' [byteLength]: 2,\n' + + ' [byteOffset]: 1,\n' + + ' [buffer]: ArrayBuffer { [Uint8Contents]: <01 02 03 04>,' + + ' [byteLength]: 4, x: 42 },\n' + ' y: 1337\n}'); } { const ab = new ArrayBuffer(42); + const dv = new DataView(ab); + assert.strictEqual(ab.byteLength, 42); new MessageChannel().port1.postMessage(ab, [ ab ]); assert.strictEqual(ab.byteLength, 0); assert.strictEqual(util.inspect(ab), - 'ArrayBuffer { (detached), byteLength: 0 }'); + 'ArrayBuffer { (detached), [byteLength]: 0 }'); + + assert.strictEqual( + util.inspect(dv), + 'DataView {\n' + + ' [byteLength]: 0,\n' + + ' [byteOffset]: undefined,\n' + + ' [buffer]: ArrayBuffer { (detached), [byteLength]: 0 }\n' + + ' }', + ); } // Truncate output for ArrayBuffers using plural or singular bytes { const ab = new ArrayBuffer(3); - assert.strictEqual(util.inspect(ab, { showHidden: true, maxArrayLength: 2 }), + assert.strictEqual(util.inspect(ab, { showHidden: true, maxArrayLength: 2, breakLength: 82 }), 'ArrayBuffer { [Uint8Contents]' + - ': <00 00 ... 1 more byte>, byteLength: 3 }'); - assert.strictEqual(util.inspect(ab, { showHidden: true, maxArrayLength: 1 }), + ': <00 00 ... 1 more byte>, [byteLength]: 3 }'); + assert.strictEqual(util.inspect(ab, { showHidden: true, maxArrayLength: 1, breakLength: 82 }), 'ArrayBuffer { [Uint8Contents]' + - ': <00 ... 2 more bytes>, byteLength: 3 }'); + ': <00 ... 2 more bytes>, [byteLength]: 3 }'); } // Now do the same checks but from a different context. @@ -231,35 +242,35 @@ assert.doesNotMatch( const dv = vm.runInNewContext('new DataView(ab, 1, 2)', { ab }); assert.strictEqual( util.inspect(ab, showHidden), - 'ArrayBuffer { [Uint8Contents]: <00 00 00 00>, byteLength: 4 }' + 'ArrayBuffer { [Uint8Contents]: <00 00 00 00>, [byteLength]: 4 }' ); assert.strictEqual(util.inspect(new DataView(ab, 1, 2), showHidden), 'DataView {\n' + - ' byteLength: 2,\n' + - ' byteOffset: 1,\n' + - ' buffer: ArrayBuffer { [Uint8Contents]: <00 00 00 00>,' + - ' byteLength: 4 }\n}'); + ' [byteLength]: 2,\n' + + ' [byteOffset]: 1,\n' + + ' [buffer]: ArrayBuffer { [Uint8Contents]: <00 00 00 00>,' + + ' [byteLength]: 4 }\n}'); assert.strictEqual( util.inspect(ab, showHidden), - 'ArrayBuffer { [Uint8Contents]: <00 00 00 00>, byteLength: 4 }' + 'ArrayBuffer { [Uint8Contents]: <00 00 00 00>, [byteLength]: 4 }' ); assert.strictEqual(util.inspect(dv, showHidden), 'DataView {\n' + - ' byteLength: 2,\n' + - ' byteOffset: 1,\n' + - ' buffer: ArrayBuffer { [Uint8Contents]: <00 00 00 00>,' + - ' byteLength: 4 }\n}'); + ' [byteLength]: 2,\n' + + ' [byteOffset]: 1,\n' + + ' [buffer]: ArrayBuffer { [Uint8Contents]: <00 00 00 00>,' + + ' [byteLength]: 4 }\n}'); ab.x = 42; dv.y = 1337; assert.strictEqual(util.inspect(ab, showHidden), 'ArrayBuffer { [Uint8Contents]: <00 00 00 00>, ' + - 'byteLength: 4, x: 42 }'); - assert.strictEqual(util.inspect(dv, showHidden), + '[byteLength]: 4, x: 42 }'); + assert.strictEqual(util.inspect(dv, { showHidden, breakLength: 82 }), 'DataView {\n' + - ' byteLength: 2,\n' + - ' byteOffset: 1,\n' + - ' buffer: ArrayBuffer { [Uint8Contents]: <00 00 00 00>,' + - ' byteLength: 4, x: 42 },\n' + + ' [byteLength]: 2,\n' + + ' [byteOffset]: 1,\n' + + ' [buffer]: ArrayBuffer { [Uint8Contents]: <00 00 00 00>,' + + ' [byteLength]: 4, x: 42 },\n' + ' y: 1337\n}'); } @@ -286,7 +297,7 @@ assert.doesNotMatch( ` [length]: ${length},\n` + ` [byteLength]: ${byteLength},\n` + ' [byteOffset]: 0,\n' + - ` [buffer]: ArrayBuffer { byteLength: ${byteLength} }\n]`); + ` [buffer]: ArrayBuffer { [byteLength]: ${byteLength} }\n]`); assert.strictEqual( util.inspect(array, false), `${constructor.name}(${length}) [ 65, 97 ]` @@ -320,7 +331,7 @@ assert.doesNotMatch( ` [length]: ${length},\n` + ` [byteLength]: ${byteLength},\n` + ' [byteOffset]: 0,\n' + - ` [buffer]: ArrayBuffer { byteLength: ${byteLength} }\n]`); + ` [buffer]: ArrayBuffer { [byteLength]: ${byteLength} }\n]`); assert.strictEqual( util.inspect(array, false), `${constructor.name}(${length}) [ 65, 97 ]` @@ -1837,7 +1848,7 @@ util.inspect(process); ' [byteLength]: 0,', ' [byteOffset]: 0,', ' [buffer]: ArrayBuffer {', - ' byteLength: 0,', + ' [byteLength]: 0,', ' foo: true', ' }', ' ],', @@ -1855,7 +1866,7 @@ util.inspect(process); ' [byteLength]: 0,', ' [byteOffset]: 0,', ' [buffer]: ArrayBuffer {', - ' byteLength: 0,', + ' [byteLength]: 0,', ' foo: true', ' }', ' ],', @@ -1885,7 +1896,7 @@ util.inspect(process); ' [length]: 0,', ' [byteLength]: 0,', ' [byteOffset]: 0,', - ' [buffer]: ArrayBuffer { byteLength: 0, foo: true }', + ' [buffer]: ArrayBuffer { [byteLength]: 0, foo: true }', ' ],', ' [Set Iterator] {', ' [ 1, 2, [length]: 2 ],', @@ -1896,7 +1907,7 @@ util.inspect(process); ' [length]: 0,', ' [byteLength]: 0,', ' [byteOffset]: 0,', - ' [buffer]: ArrayBuffer { byteLength: 0, foo: true }', + ' [buffer]: ArrayBuffer { [byteLength]: 0, foo: true }', ' ],', ' [Circular *1],', " [Symbol(Symbol.toStringTag)]: 'Map Iterator'", @@ -1924,7 +1935,7 @@ util.inspect(process); ' [byteLength]: 0,', ' [byteOffset]: 0,', ' [buffer]: ArrayBuffer {', - ' byteLength: 0,', + ' [byteLength]: 0,', ' foo: true } ],', ' [Set Iterator] {', ' [ 1,', @@ -1938,7 +1949,7 @@ util.inspect(process); ' [byteLength]: 0,', ' [byteOffset]: 0,', ' [buffer]: ArrayBuffer {', - ' byteLength: 0,', + ' [byteLength]: 0,', ' foo: true } ],', ' [Circular *1],', ' [Symbol(Symbol.toStringTag)]:', @@ -2244,12 +2255,12 @@ assert.strictEqual(util.inspect('"\'${a}'), "'\"\\'${a}'"); [new BigUint64Array(2), '[BigUint64Array(2): null prototype] [ 0n, 0n ]'], [new ArrayBuffer(16), '[ArrayBuffer: null prototype] {\n' + ' [Uint8Contents]: <00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00>,\n' + - ' byteLength: undefined\n}'], + ' [byteLength]: undefined\n}'], [new DataView(new ArrayBuffer(16)), - '[DataView: null prototype] {\n byteLength: undefined,\n ' + - 'byteOffset: undefined,\n buffer: undefined\n}'], + '[DataView: null prototype] {\n [byteLength]: undefined,\n ' + + '[byteOffset]: undefined,\n [buffer]: undefined\n}'], [new SharedArrayBuffer(2), '[SharedArrayBuffer: null prototype] ' + - '{\n [Uint8Contents]: <00 00>,\n byteLength: undefined\n}'], + '{\n [Uint8Contents]: <00 00>,\n [byteLength]: undefined\n}'], [/foobar/, '[RegExp: null prototype] /foobar/'], [new Date('Sun, 14 Feb 2010 11:48:40 GMT'), '[Date: null prototype] 2010-02-14T11:48:40.000Z'], @@ -3632,7 +3643,7 @@ assert.strictEqual( assert.strictEqual( util.inspect(o), '{\n' + - ' arrayBuffer: ArrayBuffer { [Uint8Contents]: <>, byteLength: 0 },\n' + + ' arrayBuffer: ArrayBuffer { [Uint8Contents]: <>, [byteLength]: 0 },\n' + ' buffer: ,\n' + ' typedArray: TypedArray(5) [Uint8Array] [ 72, 101, 108, 108, 111 ],\n' + ' array: [],\n' + @@ -3705,3 +3716,28 @@ ${error.stack.split('\n').slice(1).join('\n')}`, assert.strictEqual(inspect(error), '[Error: foo\n [Error: bar\n [Circular *1]]]'); } + +{ + Object.defineProperty(Error, Symbol.hasInstance, + { __proto__: null, value: common.mustNotCall(), configurable: true }); + const error = new Error(); + + const throwingGetter = { + __proto__: null, + get() { + throw error; + }, + configurable: true, + enumerable: true, + }; + + Object.defineProperties(error, { + name: throwingGetter, + stack: throwingGetter, + cause: throwingGetter, + }); + + assert.strictEqual(inspect(error), `[object Error] {\n stack: [Getter/Setter],\n name: [Getter],\n cause: [Getter]\n}`); + assert.match(inspect(DOMException.prototype), /^\[object DOMException\] \{/); + delete Error[Symbol.hasInstance]; +} diff --git a/test/parallel/test-vm-module-evaluate-source-text-module.js b/test/parallel/test-vm-module-evaluate-source-text-module.js new file mode 100644 index 00000000000000..6227e6d73d4497 --- /dev/null +++ b/test/parallel/test-vm-module-evaluate-source-text-module.js @@ -0,0 +1,167 @@ +// Flags: --experimental-vm-modules +'use strict'; + +// This tests the result of evaluating a vm.SourceTextModule. +const common = require('../common'); + +const assert = require('assert'); +// To make testing easier we just use the public inspect API. If the output format +// changes, update this test accordingly. +const { inspect } = require('util'); +const vm = require('vm'); + +globalThis.callCount = {}; +common.allowGlobals(globalThis.callCount); + +// Synchronous error during evaluation results in a synchronously rejected promise. +{ + globalThis.callCount.syncError = 0; + const mod = new vm.SourceTextModule(` + globalThis.callCount.syncError++; + throw new Error("synchronous source text module"); + export const a = 1; + `); + mod.linkRequests([]); + mod.instantiate(); + const promise = mod.evaluate(); + assert.strictEqual(globalThis.callCount.syncError, 1); + assert.match(inspect(promise), /rejected/); + assert(mod.error, 'Expected mod.error to be set'); + assert.strictEqual(mod.error.message, 'synchronous source text module'); + + promise.catch(common.mustCall((err) => { + assert.strictEqual(err, mod.error); + // Calling evaluate() again results in the same rejection synchronously. + const promise2 = mod.evaluate(); + assert.match(inspect(promise2), /rejected/); + promise2.catch(common.mustCall((err2) => { + assert.strictEqual(err, err2); + // The module is only evaluated once. + assert.strictEqual(globalThis.callCount.syncError, 1); + })); + })); +} + +// Successful evaluation of a module without top-level await results in a +// promise synchronously resolved to undefined. +{ + globalThis.callCount.syncNamedExports = 0; + const mod = new vm.SourceTextModule(` + globalThis.callCount.syncNamedExports++; + export const a = 1, b = 2; + `); + mod.linkRequests([]); + mod.instantiate(); + const promise = mod.evaluate(); + assert.match(inspect(promise), /Promise { undefined }/); + assert.strictEqual(mod.namespace.a, 1); + assert.strictEqual(mod.namespace.b, 2); + assert.strictEqual(globalThis.callCount.syncNamedExports, 1); + promise.then(common.mustCall((value) => { + assert.strictEqual(value, undefined); + + // Calling evaluate() again results in the same resolved promise synchronously. + const promise2 = mod.evaluate(); + assert.match(inspect(promise2), /Promise { undefined }/); + assert.strictEqual(mod.namespace.a, 1); + assert.strictEqual(mod.namespace.b, 2); + promise2.then(common.mustCall((value) => { + assert.strictEqual(value, undefined); + // The module is only evaluated once. + assert.strictEqual(globalThis.callCount.syncNamedExports, 1); + })); + })); +} + +{ + globalThis.callCount.syncDefaultExports = 0; + // Modules with either named and default exports have the same behaviors. + const mod = new vm.SourceTextModule(` + globalThis.callCount.syncDefaultExports++; + export default 42; + `); + mod.linkRequests([]); + mod.instantiate(); + const promise = mod.evaluate(); + assert.match(inspect(promise), /Promise { undefined }/); + assert.strictEqual(mod.namespace.default, 42); + assert.strictEqual(globalThis.callCount.syncDefaultExports, 1); + + promise.then(common.mustCall((value) => { + assert.strictEqual(value, undefined); + + // Calling evaluate() again results in the same resolved promise synchronously. + const promise2 = mod.evaluate(); + assert.match(inspect(promise2), /Promise { undefined }/); + assert.strictEqual(mod.namespace.default, 42); + promise2.then(common.mustCall((value) => { + assert.strictEqual(value, undefined); + // The module is only evaluated once. + assert.strictEqual(globalThis.callCount.syncDefaultExports, 1); + })); + })); +} + +// Successful evaluation of a module with top-level await results in a promise +// that is fulfilled asynchronously with undefined. +{ + globalThis.callCount.asyncEvaluation = 0; + const mod = new vm.SourceTextModule(` + globalThis.callCount.asyncEvaluation++; + await Promise.resolve(); + export const a = 1; + `); + mod.linkRequests([]); + mod.instantiate(); + const promise = mod.evaluate(); + assert.match(inspect(promise), //); + // Accessing the namespace before the promise is fulfilled throws ReferenceError. + assert.throws(() => mod.namespace.a, { name: 'ReferenceError' }); + assert.strictEqual(globalThis.callCount.asyncEvaluation, 1); + promise.then(common.mustCall((value) => { + assert.strictEqual(value, undefined); + assert.strictEqual(globalThis.callCount.asyncEvaluation, 1); + + // Calling evaluate() again results in a promise synchronously resolved to undefined. + const promise2 = mod.evaluate(); + assert.match(inspect(promise2), /Promise { undefined }/); + assert.strictEqual(mod.namespace.a, 1); + promise2.then(common.mustCall((value) => { + assert.strictEqual(value, undefined); + // The module is only evaluated once. + assert.strictEqual(globalThis.callCount.asyncEvaluation, 1); + })); + })); +} + +// Rejection of a top-level await promise results in a promise that is +// rejected asynchronously with the same reason. +{ + globalThis.callCount.asyncRejection = 0; + const mod = new vm.SourceTextModule(` + globalThis.callCount.asyncRejection++; + await Promise.reject(new Error("asynchronous source text module")); + export const a = 1; + `); + mod.linkRequests([]); + mod.instantiate(); + const promise = mod.evaluate(); + assert.match(inspect(promise), //); + // Accessing the namespace before the promise is fulfilled throws ReferenceError. + assert.throws(() => mod.namespace.a, { name: 'ReferenceError' }); + promise.catch(common.mustCall((err) => { + assert.strictEqual(err, mod.error); + assert.strictEqual(err.message, 'asynchronous source text module'); + assert.strictEqual(globalThis.callCount.asyncRejection, 1); + + // Calling evaluate() again results in a promise synchronously rejected + // with the same reason. + const promise2 = mod.evaluate(); + assert.match(inspect(promise2), /rejected/); + promise2.catch(common.mustCall((err2) => { + assert.strictEqual(err, err2); + // The module is only evaluated once. + assert.strictEqual(globalThis.callCount.asyncRejection, 1); + })); + })); +} diff --git a/test/parallel/test-vm-module-evaluate-synthethic-module-rejection.js b/test/parallel/test-vm-module-evaluate-synthethic-module-rejection.js new file mode 100644 index 00000000000000..ad7b2f922fbced --- /dev/null +++ b/test/parallel/test-vm-module-evaluate-synthethic-module-rejection.js @@ -0,0 +1,42 @@ +// Flags: --experimental-vm-modules +'use strict'; + +// This tests the result of evaluating a vm.SyntheticModule with an async rejection +// in the evaluation step. + +const common = require('../common'); + +const assert = require('assert'); +// To make testing easier we just use the public inspect API. If the output format +// changes, update this test accordingly. +const { inspect } = require('util'); +const vm = require('vm'); + +// The promise _synchronously_ resolves to undefined, because for a synthethic module, +// the evaluation operation can only either resolve or reject immediately. +// In this case, the asynchronously rejected promise can't be handled from the outside, +// so we'll catch it with the isolate-level unhandledRejection handler. +// See https://tc39.es/ecma262/#sec-smr-Evaluate +process.on('unhandledRejection', common.mustCall((err) => { + assert.strictEqual(err.message, 'asynchronous source text module'); +})); + +const mod = new vm.SyntheticModule(['a'], common.mustCall(async () => { + throw new Error('asynchronous source text module'); +})); + +const promise = mod.evaluate(); +assert.match(inspect(promise), /Promise { undefined }/); +// Accessing the uninitialized export of a synthetic module returns undefined. +assert.strictEqual(mod.namespace.a, undefined); + +promise.then(common.mustCall((value) => { + assert.strictEqual(value, undefined); +})); + +// Calling evaluate() again results in a promise _synchronously_ resolved to undefined again. +const promise2 = mod.evaluate(); +assert.match(inspect(promise2), /Promise { undefined }/); +promise2.then(common.mustCall((value) => { + assert.strictEqual(value, undefined); +})); diff --git a/test/parallel/test-vm-module-evaluate-synthethic-module.js b/test/parallel/test-vm-module-evaluate-synthethic-module.js new file mode 100644 index 00000000000000..1cd1bcadc6801a --- /dev/null +++ b/test/parallel/test-vm-module-evaluate-synthethic-module.js @@ -0,0 +1,83 @@ +// Flags: --experimental-vm-modules +'use strict'; + +// This tests the result of evaluating a vm.SynthethicModule. +// See https://tc39.es/ecma262/#sec-smr-Evaluate +const common = require('../common'); + +const assert = require('assert'); +// To make testing easier we just use the public inspect API. If the output format +// changes, update this test accordingly. +const { inspect } = require('util'); +const vm = require('vm'); + +// Synthetic modules with a synchronous evaluation step evaluate to a promise synchronously +// resolved to undefined. +{ + const mod = new vm.SyntheticModule(['a'], common.mustCall(() => { + mod.setExport('a', 42); + })); + const promise = mod.evaluate(); + assert.match(inspect(promise), /Promise { undefined }/); + assert.strictEqual(mod.namespace.a, 42); + + promise.then(common.mustCall((value) => { + assert.strictEqual(value, undefined); + + // Calling evaluate() again results in a promise synchronously resolved to undefined. + const promise2 = mod.evaluate(); + assert.match(inspect(promise2), /Promise { undefined }/); + promise2.then(common.mustCall((value) => { + assert.strictEqual(value, undefined); + })); + })); +} + +// Synthetic modules with an asynchronous evaluation step evaluate to a promise +// _synchronously_ resolved to undefined. +{ + const mod = new vm.SyntheticModule(['a'], common.mustCall(async () => { + const result = await Promise.resolve(42); + mod.setExport('a', result); + return result; + })); + const promise = mod.evaluate(); + assert.match(inspect(promise), /Promise { undefined }/); + // Accessing the uninitialized export of a synthetic module returns undefined. + assert.strictEqual(mod.namespace.a, undefined); + + promise.then(common.mustCall((value) => { + assert.strictEqual(value, undefined); + + // Calling evaluate() again results in a promise _synchronously_ resolved to undefined again. + const promise2 = mod.evaluate(); + assert.match(inspect(promise2), /Promise { undefined }/); + promise2.then(common.mustCall((value) => { + assert.strictEqual(value, undefined); + })); + })); +} + +// Synchronous error during the evaluation step of a synthetic module results +// in a _synchronously_ rejected promise. +{ + const mod = new vm.SyntheticModule(['a'], common.mustCall(() => { + throw new Error('synchronous synthethic module'); + })); + const promise = mod.evaluate(); + assert.match(inspect(promise), /rejected/); + assert(mod.error, 'Expected mod.error to be set'); + assert.strictEqual(mod.error.message, 'synchronous synthethic module'); + + promise.catch(common.mustCall((err) => { + assert.strictEqual(err, mod.error); + + // Calling evaluate() again results in a promise _synchronously_ rejected + // with the same reason. + const promise2 = mod.evaluate(); + assert.match(inspect(promise2), /rejected/); + promise2.catch(common.mustCall((err2) => { + assert.strictEqual(err, err2); + })); + })); +} diff --git a/test/parallel/test-vm-module-evaluate-while-evaluating.js b/test/parallel/test-vm-module-evaluate-while-evaluating.js new file mode 100644 index 00000000000000..343b67e610332b --- /dev/null +++ b/test/parallel/test-vm-module-evaluate-while-evaluating.js @@ -0,0 +1,34 @@ +// Flags: --experimental-vm-modules +'use strict'; + +// This tests the result of evaluating a vm.Module while it is evaluating. +const common = require('../common'); + +const assert = require('assert'); +const vm = require('vm'); + +{ + let mod; + globalThis.evaluate = common.mustCall(() => { + assert.rejects(() => mod.evaluate(), { + code: 'ERR_VM_MODULE_STATUS' + }).then(common.mustCall()); + }); + common.allowGlobals(globalThis.evaluate); + mod = new vm.SourceTextModule(` + globalThis.evaluate(); + export const a = 42; + `); + mod.linkRequests([]); + mod.instantiate(); + mod.evaluate(); +} + +{ + const mod = new vm.SyntheticModule(['a'], common.mustCall(() => { + assert.rejects(() => mod.evaluate(), { + code: 'ERR_VM_MODULE_STATUS' + }).then(common.mustCall()); + })); + mod.evaluate(); +} diff --git a/test/parallel/test-watch-mode-kill-signal-default.mjs b/test/parallel/test-watch-mode-kill-signal-default.mjs new file mode 100644 index 00000000000000..c3b2331e367ebb --- /dev/null +++ b/test/parallel/test-watch-mode-kill-signal-default.mjs @@ -0,0 +1,45 @@ +// Test that the kill signal sent by --watch defaults to SIGTERM. + +import '../common/index.mjs'; +import assert from 'node:assert'; +import { writeFileSync } from 'node:fs'; +import { spawn } from 'node:child_process'; +import { once } from 'node:events'; +import tmpdir from '../common/tmpdir.js'; +import fixtures from '../common/fixtures.js'; +import { skipIfNoWatchModeSignals } from '../common/watch.js'; + +skipIfNoWatchModeSignals(); + +tmpdir.refresh(); +const indexPath = tmpdir.resolve('kill-signal-for-watch.js'); +const indexContents = fixtures.readSync('kill-signal-for-watch.js', 'utf8'); +writeFileSync(indexPath, indexContents); + +const child = spawn( + process.execPath, + ['--watch', indexPath], + { + cwd: tmpdir.path, + stdio: ['inherit', 'pipe', 'inherit', 'ipc'], + } +); + +let stdout = ''; +child.stdout.on('data', (data) => { + stdout += `${data}`; + if (/__(SIGINT|SIGTERM) received__/.test(stdout)) { + child.kill(); + } +}); + +child.on('message', (msg) => { + if (msg === 'script ready') { + writeFileSync(indexPath, indexContents); + } +}); + +await once(child, 'exit'); + +assert.match(stdout, /__SIGTERM received__/); +assert.doesNotMatch(stdout, /__SIGINT received__/); diff --git a/test/parallel/test-watch-mode-kill-signal-invalid.mjs b/test/parallel/test-watch-mode-kill-signal-invalid.mjs new file mode 100644 index 00000000000000..e2f32ba978e210 --- /dev/null +++ b/test/parallel/test-watch-mode-kill-signal-invalid.mjs @@ -0,0 +1,39 @@ +// Test that --watch-kill-signal errors when an invalid kill signal is provided. + +import '../common/index.mjs'; +import assert from 'node:assert'; +import { writeFileSync } from 'node:fs'; +import { spawn } from 'node:child_process'; +import tmpdir from '../common/tmpdir.js'; +import fixtures from '../common/fixtures.js'; +import { skipIfNoWatchModeSignals } from '../common/watch.js'; + +skipIfNoWatchModeSignals(); + +tmpdir.refresh(); +const indexPath = tmpdir.resolve('kill-signal-for-watch.js'); +const indexContents = fixtures.readSync('kill-signal-for-watch.js', 'utf8'); +writeFileSync(indexPath, indexContents); + +const currentRun = Promise.withResolvers(); +const child = spawn( + process.execPath, + ['--watch', '--watch-kill-signal', 'invalid_signal', indexPath], + { + cwd: tmpdir.path, + stdio: ['inherit', 'inherit', 'pipe'], + } +); +let stderr = ''; + +child.stderr.on('data', (data) => { + stderr += data.toString(); + currentRun.resolve(); +}); + +await currentRun.promise; + +assert.match( + stderr, + /TypeError \[ERR_UNKNOWN_SIGNAL\]: Unknown signal: invalid_signal/ +); diff --git a/test/parallel/test-watch-mode-kill-signal-override.mjs b/test/parallel/test-watch-mode-kill-signal-override.mjs new file mode 100644 index 00000000000000..cc9e1219bc616e --- /dev/null +++ b/test/parallel/test-watch-mode-kill-signal-override.mjs @@ -0,0 +1,46 @@ +// Test that the kill signal sent by --watch can be overridden to SIGINT +// by using --watch-kill-signal. + +import '../common/index.mjs'; +import assert from 'node:assert'; +import { writeFileSync } from 'node:fs'; +import { spawn } from 'node:child_process'; +import { once } from 'node:events'; +import tmpdir from '../common/tmpdir.js'; +import fixtures from '../common/fixtures.js'; +import { skipIfNoWatchModeSignals } from '../common/watch.js'; + +skipIfNoWatchModeSignals(); + +tmpdir.refresh(); +const indexPath = tmpdir.resolve('kill-signal-for-watch.js'); +const indexContents = fixtures.readSync('kill-signal-for-watch.js', 'utf8'); +writeFileSync(indexPath, indexContents); + +const child = spawn( + process.execPath, + ['--watch', '--watch-kill-signal', 'SIGINT', indexPath], + { + cwd: tmpdir.path, + stdio: ['inherit', 'pipe', 'inherit', 'ipc'], + } +); + +let stdout = ''; +child.stdout.on('data', (data) => { + stdout += `${data}`; + if (/__(SIGINT|SIGTERM) received__/.test(stdout)) { + child.kill(); + } +}); + +child.on('message', (msg) => { + if (msg === 'script ready') { + writeFileSync(indexPath, indexContents); + } +}); + +await once(child, 'exit'); + +assert.match(stdout, /__SIGINT received__/); +assert.doesNotMatch(stdout, /__SIGTERM received__/); diff --git a/test/parallel/test-webcrypto-derivebits-hkdf.js b/test/parallel/test-webcrypto-derivebits-hkdf.js index 0629f85b0fb538..2759223e76a060 100644 --- a/test/parallel/test-webcrypto-derivebits-hkdf.js +++ b/test/parallel/test-webcrypto-derivebits-hkdf.js @@ -91,18 +91,20 @@ const kDerivations = { empty: '9e4b719033742101e90f1ad61e2ff3b4' + '256863667296d74389f1f02af2c4e6a6' }, - 'SHA3-256': { - normal: '386b0693d7a58c4ddf01b49bfbbd2fa87c6f911991543995170ba20ed28df599', - empty: 'd029bc828b6c6c8bb16ce3d25f5058f19c7d2517745e11c5d65c6d242e82e47f', - }, - 'SHA3-384': { - normal: '8c3b72e659bad40bcd14bdc1f7c3836059d24253795ab046a272973fd0456508', - empty: '3211ff4c676f761494c1ca2683d2d4662fe1d770ae5c58ebf6af6acb181c7d71', - }, - 'SHA3-512': { - normal: '5588c5c70cb3dd2f95323da2e9d5f299ca99c301d920a499330c449d21c645cd', - empty: '2c944b916c2751a71a1b5e57fcb487939c624335683995770b9f7cc7cbbb21f0', - }, + ...(!process.features.openssl_is_boringssl ? { + 'SHA3-256': { + normal: '386b0693d7a58c4ddf01b49bfbbd2fa87c6f911991543995170ba20ed28df599', + empty: 'd029bc828b6c6c8bb16ce3d25f5058f19c7d2517745e11c5d65c6d242e82e47f', + }, + 'SHA3-384': { + normal: '8c3b72e659bad40bcd14bdc1f7c3836059d24253795ab046a272973fd0456508', + empty: '3211ff4c676f761494c1ca2683d2d4662fe1d770ae5c58ebf6af6acb181c7d71', + }, + 'SHA3-512': { + normal: '5588c5c70cb3dd2f95323da2e9d5f299ca99c301d920a499330c449d21c645cd', + empty: '2c944b916c2751a71a1b5e57fcb487939c624335683995770b9f7cc7cbbb21f0', + }, + } : {}), }, empty: { 'SHA-384': { @@ -129,18 +131,20 @@ const kDerivations = { empty: 'c8e12774135305c9147f2cc4766e5ead' + '25d8f457b9a1953d52677361ced558fb' }, - 'SHA3-256': { - normal: '9befc557f5baf4075b5fb38c014b41b92ab7534150baf64201069e8807d0e83d', - empty: '54d1fa1aa7cad99dab0622b772170e775c103756183bac36a228fd817a98a3f6', - }, - 'SHA3-384': { - normal: '46b54c015e368677edf7ac16963bccd9d2ba8246eef0e8beb04d8d188774b91b', - empty: '46eb0b2649bb0f605d70e4818ffc8176ee1be9782396e69fb4d0fd7cfe902b55', - }, - 'SHA3-512': { - normal: 'aa4375c82b5d7a3cac88a0423250b3882f140c253e98e8e7a0f6055b0908e4c2', - empty: '6613003f98602ddb53ac35f5aa256c9f5279d50ee65bb08fdf2ecf65cc5df27f', - }, + ...(!process.features.openssl_is_boringssl ? { + 'SHA3-256': { + normal: '9befc557f5baf4075b5fb38c014b41b92ab7534150baf64201069e8807d0e83d', + empty: '54d1fa1aa7cad99dab0622b772170e775c103756183bac36a228fd817a98a3f6', + }, + 'SHA3-384': { + normal: '46b54c015e368677edf7ac16963bccd9d2ba8246eef0e8beb04d8d188774b91b', + empty: '46eb0b2649bb0f605d70e4818ffc8176ee1be9782396e69fb4d0fd7cfe902b55', + }, + 'SHA3-512': { + normal: 'aa4375c82b5d7a3cac88a0423250b3882f140c253e98e8e7a0f6055b0908e4c2', + empty: '6613003f98602ddb53ac35f5aa256c9f5279d50ee65bb08fdf2ecf65cc5df27f', + }, + } : {}), } }, long: { @@ -169,18 +173,20 @@ const kDerivations = { empty: 'e579d1f9e7f08e6f990ffcfcce1ed201' + 'c5e37e62cdf606f0ba4aca80427fbc44' }, - 'SHA3-256': { - normal: '24f38fd1905554b7cbf8395cc3976292d11ce24a0b3131da0fd4b109832d27e3', - empty: '33d0a5151c0f52e4bb7fb67cf7a17063127624dc3e685903f49ebb07872084d1', - }, - 'SHA3-384': { - normal: '15777581a1ea81ad0baac8a97d954df4142f7260d9e8351aa7f6ef6de2d04632', - empty: 'ada4da4e28dc971633a8760b265b3019db57baf17e7bf7e13cf78b1a676f6d44', - }, - 'SHA3-512': { - normal: '621e4602b07fcba55ed6b976a8bef513b0f7c4ad0c546e0f852993051d887408', - empty: 'f1292af65b05c86cf7146b739bc65785c707450316f3207ee54a3f596a7d0f7b', - }, + ...(!process.features.openssl_is_boringssl ? { + 'SHA3-256': { + normal: '24f38fd1905554b7cbf8395cc3976292d11ce24a0b3131da0fd4b109832d27e3', + empty: '33d0a5151c0f52e4bb7fb67cf7a17063127624dc3e685903f49ebb07872084d1', + }, + 'SHA3-384': { + normal: '15777581a1ea81ad0baac8a97d954df4142f7260d9e8351aa7f6ef6de2d04632', + empty: 'ada4da4e28dc971633a8760b265b3019db57baf17e7bf7e13cf78b1a676f6d44', + }, + 'SHA3-512': { + normal: '621e4602b07fcba55ed6b976a8bef513b0f7c4ad0c546e0f852993051d887408', + empty: 'f1292af65b05c86cf7146b739bc65785c707450316f3207ee54a3f596a7d0f7b', + }, + } : {}), }, empty: { 'SHA-384': { @@ -207,18 +213,20 @@ const kDerivations = { empty: 'b4f7e7557674d501cbfbc0148ad800c0' + '750189fe295a2aca5e1bf4122c85edf9' }, - 'SHA3-256': { - normal: 'fe32459f7339dd2e8df6c6fc874ed9e81e3b7aad669edad9b71196f53ed95b12', - empty: '04519be1eb94079c91306cc5b21946b3de6a78ad35ec83d4f4a37bafbda678d7', - }, - 'SHA3-384': { - normal: 'a474e8289cb4a0511e90b87eaf9ec29cadd74d4c1f2ee1fb8cb5f7d08f91a379', - empty: '726c8c4b39083a7d5755604d3a67e9aa6139db00c08028ac9e69f7fb1525bf1d', - }, - 'SHA3-512': { - normal: 'c7a7f5004d1d595c6896498c169642ac24b946e13296ff53e12b534962a88675', - empty: '7b543480b5696932551abb3100d72e05c18f57fbb63aa44fe020bef1eec3555c', - }, + ...(!process.features.openssl_is_boringssl ? { + 'SHA3-256': { + normal: 'fe32459f7339dd2e8df6c6fc874ed9e81e3b7aad669edad9b71196f53ed95b12', + empty: '04519be1eb94079c91306cc5b21946b3de6a78ad35ec83d4f4a37bafbda678d7', + }, + 'SHA3-384': { + normal: 'a474e8289cb4a0511e90b87eaf9ec29cadd74d4c1f2ee1fb8cb5f7d08f91a379', + empty: '726c8c4b39083a7d5755604d3a67e9aa6139db00c08028ac9e69f7fb1525bf1d', + }, + 'SHA3-512': { + normal: 'c7a7f5004d1d595c6896498c169642ac24b946e13296ff53e12b534962a88675', + empty: '7b543480b5696932551abb3100d72e05c18f57fbb63aa44fe020bef1eec3555c', + }, + } : {}), } }, }; diff --git a/test/parallel/test-webcrypto-derivekey.js b/test/parallel/test-webcrypto-derivekey.js index 422384f4447bda..e04a7eab1bd8ef 100644 --- a/test/parallel/test-webcrypto-derivekey.js +++ b/test/parallel/test-webcrypto-derivekey.js @@ -135,14 +135,21 @@ const { KeyObject } = require('crypto'); '201509b012c9cd2fbe7ea938f0c509b36ecb140f38bf9130e96923f55f46756d'], ['hello', 'there', 5, 'SHA-512', '2e8d981741f98193e0af9c79870af0e985089341221edad9a130d297eae1984b'], - ['hello', 'there', 5, 'SHA3-256', - '0aed29b61b3ca3978aea34a9793276574ea997b69e8d03727438199f90571649'], - ['hello', 'there', 5, 'SHA3-384', - '7aa4a274aa19b4623c5d3091c4b06355de85ff6f25e53a83e3126cbb86ae68df'], - ['hello', 'there', 5, 'SHA3-512', - '4d909c47a81c625f866d1f9406248e6bc3c7ea89225fbccf1f08820254c9ef56'], ]; + if (!process.features.openssl_is_boringssl) { + kTests.push( + ['hello', 'there', 5, 'SHA3-256', + '0aed29b61b3ca3978aea34a9793276574ea997b69e8d03727438199f90571649'], + ['hello', 'there', 5, 'SHA3-384', + '7aa4a274aa19b4623c5d3091c4b06355de85ff6f25e53a83e3126cbb86ae68df'], + ['hello', 'there', 5, 'SHA3-512', + '4d909c47a81c625f866d1f9406248e6bc3c7ea89225fbccf1f08820254c9ef56'] + ); + } else { + common.printSkipMessage('Skipping unsupported SHA-3 test cases'); + } + const tests = Promise.all(kTests.map((args) => test(...args))); tests.then(common.mustCall()); @@ -158,16 +165,23 @@ const { KeyObject } = require('crypto'); // Not long enough secret generated by ECDH [{ name: 'HMAC', hash: 'SHA-384' }, 'sign', 1024], [{ name: 'HMAC', hash: 'SHA-512' }, 'sign', 1024], - [{ name: 'HMAC', hash: 'SHA3-256', length: 256 }, 'sign', 256], - [{ name: 'HMAC', hash: 'SHA3-384', length: 384 }, 'sign', 384], - [{ name: 'HMAC', hash: 'SHA3-512', length: 512 }, 'sign', 512], - // This interaction is not defined for now. - // https://github.com/WICG/webcrypto-modern-algos/issues/23 - // [{ name: 'HMAC', hash: 'SHA3-256' }, 'sign', 256], - // [{ name: 'HMAC', hash: 'SHA3-384' }, 'sign', 384], - // [{ name: 'HMAC', hash: 'SHA3-512' }, 'sign', 512], ]; + if (!process.features.openssl_is_boringssl) { + vectors.push( + [{ name: 'HMAC', hash: 'SHA3-256', length: 256 }, 'sign', 256], + [{ name: 'HMAC', hash: 'SHA3-384', length: 384 }, 'sign', 384], + [{ name: 'HMAC', hash: 'SHA3-512', length: 512 }, 'sign', 512] + // This interaction is not defined for now. + // https://github.com/WICG/webcrypto-modern-algos/issues/23 + // [{ name: 'HMAC', hash: 'SHA3-256' }, 'sign', 256], + // [{ name: 'HMAC', hash: 'SHA3-384' }, 'sign', 384], + // [{ name: 'HMAC', hash: 'SHA3-512' }, 'sign', 512], + ); + } else { + common.printSkipMessage('Skipping unsupported SHA-3 test cases'); + } + if (hasOpenSSL(3)) { vectors.push( ['KMAC128', 'sign', 128], @@ -211,16 +225,23 @@ const { KeyObject } = require('crypto'); [{ name: 'HMAC', hash: 'SHA-256' }, 'sign', 512], [{ name: 'HMAC', hash: 'SHA-384' }, 'sign', 1024], [{ name: 'HMAC', hash: 'SHA-512' }, 'sign', 1024], - [{ name: 'HMAC', hash: 'SHA3-256', length: 256 }, 'sign', 256], - [{ name: 'HMAC', hash: 'SHA3-384', length: 384 }, 'sign', 384], - [{ name: 'HMAC', hash: 'SHA3-512', length: 512 }, 'sign', 512], - // This interaction is not defined for now. - // https://github.com/WICG/webcrypto-modern-algos/issues/23 - // [{ name: 'HMAC', hash: 'SHA3-256' }, 'sign', 256], - // [{ name: 'HMAC', hash: 'SHA3-384' }, 'sign', 384], - // [{ name: 'HMAC', hash: 'SHA3-512' }, 'sign', 512], ]; + if (!process.features.openssl_is_boringssl) { + vectors.push( + [{ name: 'HMAC', hash: 'SHA3-256', length: 256 }, 'sign', 256], + [{ name: 'HMAC', hash: 'SHA3-384', length: 384 }, 'sign', 384], + [{ name: 'HMAC', hash: 'SHA3-512', length: 512 }, 'sign', 512], + // This interaction is not defined for now. + // https://github.com/WICG/webcrypto-modern-algos/issues/23 + // [{ name: 'HMAC', hash: 'SHA3-256' }, 'sign', 256], + // [{ name: 'HMAC', hash: 'SHA3-384' }, 'sign', 384], + // [{ name: 'HMAC', hash: 'SHA3-512' }, 'sign', 512], + ); + } else { + common.printSkipMessage('Skipping unsupported SHA-3 test cases'); + } + if (hasOpenSSL(3)) { vectors.push( ['KMAC128', 'sign', 128], diff --git a/test/parallel/test-webcrypto-digest.js b/test/parallel/test-webcrypto-digest.js index 04507d77b59142..e91214047dea43 100644 --- a/test/parallel/test-webcrypto-digest.js +++ b/test/parallel/test-webcrypto-digest.js @@ -148,65 +148,67 @@ const kDigestedData = { '60b22aab8d36a4c2a3affdb71234f49276737c575ddf7' + '4d14054cbd6fdb98fd0ddcbcb46f91ad76b6ee' }, - 'cshake128': { - empty: '7f9c2ba4e88f827d616045507605853ed73b8093f6e' + - 'fbc88eb1a6eacfa66ef26', - short: 'dea62d73e6b59cf725d0320d660089a4475cbbd3b85' + - '39e36691f150d47556794', - medium: 'b1acd53a03e76a221e52ea578e042f686a68c3d1c9' + - '832ab18285cf4f304ca32d', - long: '3a5bf5676955e5dec87d430e526925558971ca14c370' + - 'ee5d7cf572b94c7c63d7' - }, - 'cshake256': { - empty: '46b9dd2b0ba88d13233b3feb743eeb243fcd52ea62b' + - '81b82b50c27646ed5762fd75dc4ddd8c0f200cb0501' + - '9d67b592f6fc821c49479ab48640292eacb3b7c4be', - short: '1738113f5abb3ee5320ee18aa266c3617a7475dbd8e' + - 'd9a985994fddd6112ad999ec8e2ebdfeafb96e76f6b' + - 'b3a3adba43da60f00cd12496df5af3e28ae6d3de42', - medium: '4146c13d86d9bc186b0b309ab6a124ee0c74ba26b8' + - 'c60dcc7b3ed505969aa8d19028c6317999a085b1e6' + - 'b6a785ce4ff632aeb27493227e44232fb7b3952141' + - '7b', - long: '0c42bfd1e282622fd8144aa29b072fd09fc2bae70885' + - 'd5290933492f9d17411926a613dd0611668c2ac999e8' + - 'c011aabaa9004323425fbad75b0f58ee6e777a94' - }, - 'sha3-256': { - empty: 'a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a', - short: '3059af7aa33b517084e8ad7bbc4fb208a44c28ef32b4698d103dd540e4f91aa1', - medium: '1fa7cd1da74cd8046417508c8314e74a9a4a9d38f9f18e6cb215b8c891a0a80e', - long: 'b2cfc61e0386cdaef5e10a2be189891f5ef52a7624bfcd8edc893acc64fec600' - }, - 'sha3-384': { - empty: '0c63a75b845e4f7d01107d852e4c2485c51a50aaaa9' + - '4fc61995e71bbee983a2ac3713831264adb47fb6bd1' + - 'e058d5f004', - short: '54b8f0e4cf4974de740098f66b3024479b01631315a' + - '6773606c33eadc32556a6e778e08f0225ae79265aec' + - '666cb2390b', - medium: '437b7d8b68b250b5c1739ea4cc86db2033879dfb18' + - 'de292c9c50d9c193a4c79a08a6cae3f4e483c2795e' + - 'a5d1ef7e69d2', - long: '3b39c4c97ad87613305d0ccc987181713e2d5e84b1f9' + - '760011bcce0c297499005bdce8a3d2409b5ad0164f32' + - 'bb8778d0' - }, - 'sha3-512': { - empty: 'a69f73cca23a9ac5c8b567dc185a756e97c982164fe' + - '25859e0d1dcc1475c80a615b2123af1f5f94c11e3e9' + - '402c3ac558f500199d95b6d3e301758586281dcd26', - short: '2dd2e07a62e6ad0498ba84f313c4d4024cb46001f78' + - 'f75db336b0d4d8bd2a9ec152c4ad20878735d82ba08' + - '72ecf59608ef3ced2b2a8669427e7da31e362333d8', - medium: 'e640a21909536640369e9b0a48931c5cb2efcbc91f' + - 'ecf247306bc96a0e4ca33307cb8e1b9af367946dd01' + - 'c243f3907508d04f1692a3161df1f898de8ee25febe', - long: 'bd262cecf565c338032de5ba0138f0aacfe7dde83d27' + - '2d0d37d952829ed25de1a1342d98659ef7d2fa4aca7c' + - 'e2b1aa0784d8fc1dcbf81bcec7a7431a3da36bf7' - } + ...(!process.features.openssl_is_boringssl ? { + 'cshake128': { + empty: '7f9c2ba4e88f827d616045507605853ed73b8093f6e' + + 'fbc88eb1a6eacfa66ef26', + short: 'dea62d73e6b59cf725d0320d660089a4475cbbd3b85' + + '39e36691f150d47556794', + medium: 'b1acd53a03e76a221e52ea578e042f686a68c3d1c9' + + '832ab18285cf4f304ca32d', + long: '3a5bf5676955e5dec87d430e526925558971ca14c370' + + 'ee5d7cf572b94c7c63d7' + }, + 'cshake256': { + empty: '46b9dd2b0ba88d13233b3feb743eeb243fcd52ea62b' + + '81b82b50c27646ed5762fd75dc4ddd8c0f200cb0501' + + '9d67b592f6fc821c49479ab48640292eacb3b7c4be', + short: '1738113f5abb3ee5320ee18aa266c3617a7475dbd8e' + + 'd9a985994fddd6112ad999ec8e2ebdfeafb96e76f6b' + + 'b3a3adba43da60f00cd12496df5af3e28ae6d3de42', + medium: '4146c13d86d9bc186b0b309ab6a124ee0c74ba26b8' + + 'c60dcc7b3ed505969aa8d19028c6317999a085b1e6' + + 'b6a785ce4ff632aeb27493227e44232fb7b3952141' + + '7b', + long: '0c42bfd1e282622fd8144aa29b072fd09fc2bae70885' + + 'd5290933492f9d17411926a613dd0611668c2ac999e8' + + 'c011aabaa9004323425fbad75b0f58ee6e777a94' + }, + 'sha3-256': { + empty: 'a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a', + short: '3059af7aa33b517084e8ad7bbc4fb208a44c28ef32b4698d103dd540e4f91aa1', + medium: '1fa7cd1da74cd8046417508c8314e74a9a4a9d38f9f18e6cb215b8c891a0a80e', + long: 'b2cfc61e0386cdaef5e10a2be189891f5ef52a7624bfcd8edc893acc64fec600' + }, + 'sha3-384': { + empty: '0c63a75b845e4f7d01107d852e4c2485c51a50aaaa9' + + '4fc61995e71bbee983a2ac3713831264adb47fb6bd1' + + 'e058d5f004', + short: '54b8f0e4cf4974de740098f66b3024479b01631315a' + + '6773606c33eadc32556a6e778e08f0225ae79265aec' + + '666cb2390b', + medium: '437b7d8b68b250b5c1739ea4cc86db2033879dfb18' + + 'de292c9c50d9c193a4c79a08a6cae3f4e483c2795e' + + 'a5d1ef7e69d2', + long: '3b39c4c97ad87613305d0ccc987181713e2d5e84b1f9' + + '760011bcce0c297499005bdce8a3d2409b5ad0164f32' + + 'bb8778d0' + }, + 'sha3-512': { + empty: 'a69f73cca23a9ac5c8b567dc185a756e97c982164fe' + + '25859e0d1dcc1475c80a615b2123af1f5f94c11e3e9' + + '402c3ac558f500199d95b6d3e301758586281dcd26', + short: '2dd2e07a62e6ad0498ba84f313c4d4024cb46001f78' + + 'f75db336b0d4d8bd2a9ec152c4ad20878735d82ba08' + + '72ecf59608ef3ced2b2a8669427e7da31e362333d8', + medium: 'e640a21909536640369e9b0a48931c5cb2efcbc91f' + + 'ecf247306bc96a0e4ca33307cb8e1b9af367946dd01' + + 'c243f3907508d04f1692a3161df1f898de8ee25febe', + long: 'bd262cecf565c338032de5ba0138f0aacfe7dde83d27' + + '2d0d37d952829ed25de1a1342d98659ef7d2fa4aca7c' + + 'e2b1aa0784d8fc1dcbf81bcec7a7431a3da36bf7' + } + } : {}), }; async function testDigest(size, alg) { diff --git a/test/parallel/test-webcrypto-sign-verify-rsa.js b/test/parallel/test-webcrypto-sign-verify-rsa.js index 7e90388cc4c270..7dd5637b12f702 100644 --- a/test/parallel/test-webcrypto-sign-verify-rsa.js +++ b/test/parallel/test-webcrypto-sign-verify-rsa.js @@ -241,13 +241,13 @@ async function testSaltLength(keyLength, hash, hLen) { ['SHA-256', 32], ['SHA-384', 48], ['SHA-512', 64], - ['SHA3-256', 32], - ['SHA3-384', 48], - ['SHA3-512', 64], + ...(!process.features.openssl_is_boringssl ? [ + ['SHA3-256', 32], + ['SHA3-384', 48], + ['SHA3-512', 64], + ] : []), ]) { - if (hash.startsWith('SHA-3') && !process.features.openssl_is_boringssl) { - variations.push(testSaltLength(keyLength, hash, hLen)); - } + variations.push(testSaltLength(keyLength, hash, hLen)); } } diff --git a/test/parallel/test-without-async-context-frame.mjs b/test/parallel/test-without-async-context-frame.mjs deleted file mode 100644 index fa431c5700053c..00000000000000 --- a/test/parallel/test-without-async-context-frame.mjs +++ /dev/null @@ -1,64 +0,0 @@ -import { isWindows } from '../common/index.mjs'; -import { spawn } from 'node:child_process'; -import { once } from 'node:events'; -import { opendir } from 'node:fs/promises'; -import { fileURLToPath } from 'node:url'; -import { describe, it } from 'node:test'; -import { sep } from 'node:path'; -import { strictEqual } from 'node:assert'; - -const python = process.env.PYTHON || (isWindows ? 'python' : 'python3'); - -const testRunner = fileURLToPath( - new URL('../../tools/test.py', import.meta.url) -); - -const setNames = ['async-hooks', 'parallel']; - -// Get all test names for each set -const testSets = await Promise.all(setNames.map(async (name) => { - const path = fileURLToPath(new URL(`../${name}`, import.meta.url)); - const dir = await opendir(path); - - const tests = []; - for await (const entry of dir) { - if (entry.name.startsWith('test-async-local-storage-')) { - tests.push(entry.name); - } - } - - return { - name, - tests - }; -})); - -// Merge test sets with set name prefix -const tests = testSets.reduce((m, v) => { - for (const test of v.tests) { - m.push(`${v.name}${sep}${test}`); - } - return m; -}, []); - -describe('without AsyncContextFrame', { - // TODO(qard): I think high concurrency causes memory problems on Windows - // concurrency: tests.length -}, () => { - for (const test of tests) { - it(test, async () => { - const proc = spawn(python, [ - testRunner, - `--mode=${process.features.debug ? 'debug' : 'release'}`, - `--shell=${process.execPath}`, - '--node-args=--no-async-context-frame', - test, - ], { - stdio: ['ignore', 'ignore', 'inherit'], - }); - - const [code] = await once(proc, 'exit'); - strictEqual(code, 0, `Test ${test} failed with exit code ${code}`); - }); - } -}); diff --git a/test/sea/sea.status b/test/sea/sea.status new file mode 100644 index 00000000000000..56202f3564918b --- /dev/null +++ b/test/sea/sea.status @@ -0,0 +1,20 @@ +prefix sea + +# To mark a test as flaky, list the test name in the appropriate section +# below, without ".js", followed by ": PASS,FLAKY". Example: +# sample-test : PASS,FLAKY + +[true] # This section applies to all platforms + +[$system==macos && $arch==x64] +# https://github.com/nodejs/node/issues/59553 +test-single-executable-application*: SKIP + +[$system==linux && $arch==ppc64] +# https://github.com/nodejs/node/issues/59561 +test-single-executable-application*: SKIP + +[$system==win32] +# https://github.com/nodejs/node/issues/49630 +test-single-executable-application-snapshot: PASS, FLAKY +test-single-executable-application-snapshot-and-code-cache: PASS, FLAKY diff --git a/test/sequential/test-single-executable-application-asset-keys-empty.js b/test/sea/test-single-executable-application-asset-keys-empty.js similarity index 97% rename from test/sequential/test-single-executable-application-asset-keys-empty.js rename to test/sea/test-single-executable-application-asset-keys-empty.js index 77892699a95794..9168f2b27235be 100644 --- a/test/sequential/test-single-executable-application-asset-keys-empty.js +++ b/test/sea/test-single-executable-application-asset-keys-empty.js @@ -43,7 +43,7 @@ spawnSyncAndExitWithoutError( NODE_DEBUG_NATIVE: 'SEA', ...process.env, }, - cwd: tmpdir.path + cwd: tmpdir.path, }, {}); @@ -57,9 +57,9 @@ spawnSyncAndAssert( env: { ...process.env, NODE_DEBUG_NATIVE: 'SEA', - } + }, }, { stdout: /Asset keys: \[\]/, - } + }, ); diff --git a/test/sequential/test-single-executable-application-asset-keys.js b/test/sea/test-single-executable-application-asset-keys.js similarity index 98% rename from test/sequential/test-single-executable-application-asset-keys.js rename to test/sea/test-single-executable-application-asset-keys.js index e210f61579b342..a2d1dce487e356 100644 --- a/test/sequential/test-single-executable-application-asset-keys.js +++ b/test/sea/test-single-executable-application-asset-keys.js @@ -52,7 +52,7 @@ spawnSyncAndExitWithoutError( NODE_DEBUG_NATIVE: 'SEA', ...process.env, }, - cwd: tmpdir.path + cwd: tmpdir.path, }, {}); @@ -66,9 +66,9 @@ spawnSyncAndAssert( env: { ...process.env, NODE_DEBUG_NATIVE: 'SEA', - } + }, }, { stdout: /Asset keys: \["asset-1\.txt","asset-2\.txt","asset-3\.txt"\]/, - } + }, ); diff --git a/test/sequential/test-single-executable-application-assets-raw.js b/test/sea/test-single-executable-application-assets-raw.js similarity index 98% rename from test/sequential/test-single-executable-application-assets-raw.js rename to test/sea/test-single-executable-application-assets-raw.js index a537f4c7d4f5aa..ff0fb3ecacddb3 100644 --- a/test/sequential/test-single-executable-application-assets-raw.js +++ b/test/sea/test-single-executable-application-assets-raw.js @@ -50,7 +50,7 @@ const outputFile = tmpdir.resolve(process.platform === 'win32' ? 'sea.exe' : 'se NODE_DEBUG_NATIVE: 'SEA', ...process.env, }, - cwd: tmpdir.path + cwd: tmpdir.path, }); assert(existsSync(seaPrepBlob)); @@ -64,7 +64,7 @@ const outputFile = tmpdir.resolve(process.platform === 'win32' ? 'sea.exe' : 'se ...process.env, NODE_DEBUG_NATIVE: 'SEA', __TEST_PERSON_JPG: fixtures.path('person.jpg'), - } + }, }, ); } diff --git a/test/sequential/test-single-executable-application-assets.js b/test/sea/test-single-executable-application-assets.js similarity index 96% rename from test/sequential/test-single-executable-application-assets.js rename to test/sea/test-single-executable-application-assets.js index 5d2df60e9fdaf9..dd285021cd714b 100644 --- a/test/sequential/test-single-executable-application-assets.js +++ b/test/sea/test-single-executable-application-assets.js @@ -45,12 +45,12 @@ const outputFile = tmpdir.resolve(process.platform === 'win32' ? 'sea.exe' : 'se process.execPath, ['--experimental-sea-config', 'sea-config.json'], { - cwd: tmpdir.path + cwd: tmpdir.path, }, { status: 1, signal: null, - stderr: /"assets" field of sea-config\.json is not a map of strings/ + stderr: /"assets" field of sea-config\.json is not a map of strings/, }); } @@ -71,12 +71,12 @@ const outputFile = tmpdir.resolve(process.platform === 'win32' ? 'sea.exe' : 'se process.execPath, ['--experimental-sea-config', 'sea-config.json'], { - cwd: tmpdir.path + cwd: tmpdir.path, }, { status: 1, signal: null, - stderr: /Cannot read asset nonexistent\.txt: no such file or directory/ + stderr: /Cannot read asset nonexistent\.txt: no such file or directory/, }); } @@ -104,7 +104,7 @@ const outputFile = tmpdir.resolve(process.platform === 'win32' ? 'sea.exe' : 'se NODE_DEBUG_NATIVE: 'SEA', ...process.env, }, - cwd: tmpdir.path + cwd: tmpdir.path, }); assert(existsSync(seaPrepBlob)); @@ -119,11 +119,11 @@ const outputFile = tmpdir.resolve(process.platform === 'win32' ? 'sea.exe' : 'se NODE_DEBUG_NATIVE: 'SEA', __TEST_PERSON_JPG: fixtures.path('person.jpg'), __TEST_UTF8_TEXT_PATH: fixtures.path('utf8_test_text.txt'), - } + }, }, { trim: true, stdout: fixtures.utf8TestText, - } + }, ); } diff --git a/test/sequential/test-single-executable-application-disable-experimental-sea-warning.js b/test/sea/test-single-executable-application-disable-experimental-sea-warning.js similarity index 97% rename from test/sequential/test-single-executable-application-disable-experimental-sea-warning.js rename to test/sea/test-single-executable-application-disable-experimental-sea-warning.js index 3f7c54b77360bc..b85a2f2e09f05e 100644 --- a/test/sequential/test-single-executable-application-disable-experimental-sea-warning.js +++ b/test/sea/test-single-executable-application-disable-experimental-sea-warning.js @@ -60,8 +60,8 @@ spawnSyncAndAssert( COMMON_DIRECTORY: join(__dirname, '..', 'common'), NODE_DEBUG_NATIVE: 'SEA', ...process.env, - } + }, }, { - stdout: 'Hello, world! 😊\n' + stdout: 'Hello, world! 😊\n', }); diff --git a/test/sequential/test-single-executable-application-empty.js b/test/sea/test-single-executable-application-empty.js similarity index 99% rename from test/sequential/test-single-executable-application-empty.js rename to test/sea/test-single-executable-application-empty.js index f90e52f0c704c5..6bffc94dbefeb1 100644 --- a/test/sequential/test-single-executable-application-empty.js +++ b/test/sea/test-single-executable-application-empty.js @@ -59,5 +59,5 @@ spawnSyncAndExitWithoutError( env: { NODE_DEBUG_NATIVE: 'SEA', ...process.env, - } + }, }); diff --git a/test/sequential/test-single-executable-application-exec-argv-empty.js b/test/sea/test-single-executable-application-exec-argv-empty.js similarity index 96% rename from test/sequential/test-single-executable-application-exec-argv-empty.js rename to test/sea/test-single-executable-application-exec-argv-empty.js index 953d0d0e9f871b..acae5e8eb50b37 100644 --- a/test/sequential/test-single-executable-application-exec-argv-empty.js +++ b/test/sea/test-single-executable-application-exec-argv-empty.js @@ -54,8 +54,8 @@ spawnSyncAndAssert( COMMON_DIRECTORY: join(__dirname, '..', 'common'), NODE_DEBUG_NATIVE: 'SEA', ...process.env, - } + }, }, { - stdout: /empty execArgv test passed/ + stdout: /empty execArgv test passed/, }); diff --git a/test/sequential/test-single-executable-application-exec-argv-extension-cli.js b/test/sea/test-single-executable-application-exec-argv-extension-cli.js similarity index 96% rename from test/sequential/test-single-executable-application-exec-argv-extension-cli.js rename to test/sea/test-single-executable-application-exec-argv-extension-cli.js index 81ff05d53ce7a4..fcd8602fc763f4 100644 --- a/test/sequential/test-single-executable-application-exec-argv-extension-cli.js +++ b/test/sea/test-single-executable-application-exec-argv-extension-cli.js @@ -56,8 +56,8 @@ spawnSyncAndAssert( NODE_OPTIONS: '--max-old-space-size=2048', // Should be ignored COMMON_DIRECTORY: join(__dirname, '..', 'common'), NODE_DEBUG_NATIVE: 'SEA', - } + }, }, { - stdout: /execArgvExtension cli test passed/ + stdout: /execArgvExtension cli test passed/, }); diff --git a/test/sequential/test-single-executable-application-exec-argv-extension-env.js b/test/sea/test-single-executable-application-exec-argv-extension-env.js similarity index 98% rename from test/sequential/test-single-executable-application-exec-argv-extension-env.js rename to test/sea/test-single-executable-application-exec-argv-extension-env.js index 25d07bdc468f9a..1bacba1fb0a34c 100644 --- a/test/sequential/test-single-executable-application-exec-argv-extension-env.js +++ b/test/sea/test-single-executable-application-exec-argv-extension-env.js @@ -56,7 +56,7 @@ spawnSyncAndAssert( NODE_OPTIONS: '--max-old-space-size=512', COMMON_DIRECTORY: join(__dirname, '..', 'common'), NODE_DEBUG_NATIVE: 'SEA', - } + }, }, { stdout: /execArgvExtension env test passed/, @@ -64,5 +64,5 @@ spawnSyncAndAssert( assert.doesNotMatch(output, /This warning should not be shown in the output/); return true; }, - trim: true + trim: true, }); diff --git a/test/sequential/test-single-executable-application-exec-argv-extension-none.js b/test/sea/test-single-executable-application-exec-argv-extension-none.js similarity index 96% rename from test/sequential/test-single-executable-application-exec-argv-extension-none.js rename to test/sea/test-single-executable-application-exec-argv-extension-none.js index 272016f006f3f7..c4036bc503382c 100644 --- a/test/sequential/test-single-executable-application-exec-argv-extension-none.js +++ b/test/sea/test-single-executable-application-exec-argv-extension-none.js @@ -56,8 +56,8 @@ spawnSyncAndAssert( NODE_OPTIONS: '--max-old-space-size=2048', COMMON_DIRECTORY: join(__dirname, '..', 'common'), NODE_DEBUG_NATIVE: 'SEA', - } + }, }, { - stdout: /execArgvExtension none test passed/ + stdout: /execArgvExtension none test passed/, }); diff --git a/test/sequential/test-single-executable-application-exec-argv.js b/test/sea/test-single-executable-application-exec-argv.js similarity index 99% rename from test/sequential/test-single-executable-application-exec-argv.js rename to test/sea/test-single-executable-application-exec-argv.js index f24934a0fffc6f..a60dbe1cab5eaf 100644 --- a/test/sequential/test-single-executable-application-exec-argv.js +++ b/test/sea/test-single-executable-application-exec-argv.js @@ -55,7 +55,7 @@ spawnSyncAndAssert( NODE_NO_WARNINGS: '0', COMMON_DIRECTORY: join(__dirname, '..', 'common'), NODE_DEBUG_NATIVE: 'SEA', - } + }, }, { stdout: /multiple execArgv test passed/, diff --git a/test/sequential/test-single-executable-application-inspect-in-sea-flags.js b/test/sea/test-single-executable-application-inspect-in-sea-flags.js similarity index 98% rename from test/sequential/test-single-executable-application-inspect-in-sea-flags.js rename to test/sea/test-single-executable-application-inspect-in-sea-flags.js index 32a0ad34848e80..66faed4812dba2 100644 --- a/test/sequential/test-single-executable-application-inspect-in-sea-flags.js +++ b/test/sea/test-single-executable-application-inspect-in-sea-flags.js @@ -38,7 +38,7 @@ writeFileSync(configFile, ` spawnSyncAndExitWithoutError( process.execPath, ['--experimental-sea-config', 'sea-config.json'], - { cwd: tmpdir.path } + { cwd: tmpdir.path }, ); assert(existsSync(seaPrepBlob)); @@ -65,5 +65,5 @@ spawnSyncAndAssert( return true; }, trim: true, - } + }, ); diff --git a/test/sequential/test-single-executable-application-inspect.js b/test/sea/test-single-executable-application-inspect.js similarity index 99% rename from test/sequential/test-single-executable-application-inspect.js rename to test/sea/test-single-executable-application-inspect.js index 009621ddf78b4d..688b8386eadc2d 100644 --- a/test/sequential/test-single-executable-application-inspect.js +++ b/test/sea/test-single-executable-application-inspect.js @@ -38,7 +38,7 @@ writeFileSync(configFile, ` spawnSyncAndExitWithoutError( process.execPath, ['--experimental-sea-config', 'sea-config.json'], - { cwd: tmpdir.path } + { cwd: tmpdir.path }, ); assert(existsSync(seaPrepBlob)); diff --git a/test/sequential/test-single-executable-application-snapshot-and-code-cache.js b/test/sea/test-single-executable-application-snapshot-and-code-cache.js similarity index 98% rename from test/sequential/test-single-executable-application-snapshot-and-code-cache.js rename to test/sea/test-single-executable-application-snapshot-and-code-cache.js index 6277381b3bde1a..24e10a1c12d6be 100644 --- a/test/sequential/test-single-executable-application-snapshot-and-code-cache.js +++ b/test/sea/test-single-executable-application-snapshot-and-code-cache.js @@ -56,8 +56,8 @@ const outputFile = join(tmpdir.path, process.platform === 'win32' ? 'sea.exe' : }, }, { - stderr: /"useCodeCache" is redundant when "useSnapshot" is true/ - } + stderr: /"useCodeCache" is redundant when "useSnapshot" is true/, + }, ); assert(existsSync(seaPrepBlob)); @@ -70,7 +70,7 @@ const outputFile = join(tmpdir.path, process.platform === 'win32' ? 'sea.exe' : env: { NODE_DEBUG_NATIVE: 'SEA,MKSNAPSHOT', ...process.env, - } + }, }, { stdout: 'Hello from snapshot', trim: true, diff --git a/test/sequential/test-single-executable-application-snapshot-worker.js b/test/sea/test-single-executable-application-snapshot-worker.js similarity index 97% rename from test/sequential/test-single-executable-application-snapshot-worker.js rename to test/sea/test-single-executable-application-snapshot-worker.js index 50c77743573a44..75ae6a27098798 100644 --- a/test/sequential/test-single-executable-application-snapshot-worker.js +++ b/test/sea/test-single-executable-application-snapshot-worker.js @@ -70,11 +70,11 @@ const outputFile = tmpdir.resolve(process.platform === 'win32' ? 'sea.exe' : 'se env: { NODE_DEBUG_NATIVE: 'SEA', ...process.env, - } + }, }, { trim: true, - stdout: 'Hello from Worker' - } + stdout: 'Hello from Worker', + }, ); } diff --git a/test/sequential/test-single-executable-application-snapshot.js b/test/sea/test-single-executable-application-snapshot.js similarity index 95% rename from test/sequential/test-single-executable-application-snapshot.js rename to test/sea/test-single-executable-application-snapshot.js index 552c86a0a1d9dd..15adcdb9b814bf 100644 --- a/test/sequential/test-single-executable-application-snapshot.js +++ b/test/sea/test-single-executable-application-snapshot.js @@ -39,12 +39,12 @@ const outputFile = tmpdir.resolve(process.platform === 'win32' ? 'sea.exe' : 'se process.execPath, ['--experimental-sea-config', 'sea-config.json'], { - cwd: tmpdir.path + cwd: tmpdir.path, }, { status: 1, signal: null, - stderr: /snapshot\.js does not invoke v8\.startupSnapshot\.setDeserializeMainFunction\(\)/ + stderr: /snapshot\.js does not invoke v8\.startupSnapshot\.setDeserializeMainFunction\(\)/, }); } @@ -80,7 +80,7 @@ const outputFile = tmpdir.resolve(process.platform === 'win32' ? 'sea.exe' : 'se }, }, { - stderr: /Single executable application is an experimental feature/ + stderr: /Single executable application is an experimental feature/, }); assert(existsSync(seaPrepBlob)); @@ -93,7 +93,7 @@ const outputFile = tmpdir.resolve(process.platform === 'win32' ? 'sea.exe' : 'se env: { NODE_DEBUG_NATIVE: 'SEA,MKSNAPSHOT', ...process.env, - } + }, }, { trim: true, @@ -102,7 +102,7 @@ const outputFile = tmpdir.resolve(process.platform === 'win32' ? 'sea.exe' : 'se assert.doesNotMatch( output, /Single executable application is an experimental feature/); - } - } + }, + }, ); } diff --git a/test/sequential/test-single-executable-application-use-code-cache.js b/test/sea/test-single-executable-application-use-code-cache.js similarity index 97% rename from test/sequential/test-single-executable-application-use-code-cache.js rename to test/sea/test-single-executable-application-use-code-cache.js index 6d1575ecc6fac5..a66930f610ea1f 100644 --- a/test/sequential/test-single-executable-application-use-code-cache.js +++ b/test/sea/test-single-executable-application-use-code-cache.js @@ -66,8 +66,8 @@ spawnSyncAndAssert( COMMON_DIRECTORY: join(__dirname, '..', 'common'), NODE_DEBUG_NATIVE: 'SEA', ...process.env, - } + }, }, { - stdout: 'Hello, world! 😊\n' + stdout: 'Hello, world! 😊\n', }); diff --git a/test/sequential/test-single-executable-application.js b/test/sea/test-single-executable-application.js similarity index 97% rename from test/sequential/test-single-executable-application.js rename to test/sea/test-single-executable-application.js index 2f8c346b95702e..036589c1cb269a 100644 --- a/test/sequential/test-single-executable-application.js +++ b/test/sea/test-single-executable-application.js @@ -59,8 +59,8 @@ spawnSyncAndAssert( COMMON_DIRECTORY: join(__dirname, '..', 'common'), NODE_DEBUG_NATIVE: 'SEA', ...process.env, - } + }, }, { - stdout: 'Hello, world! 😊\n' + stdout: 'Hello, world! 😊\n', }); diff --git a/test/parallel/test-single-executable-blob-config-errors.js b/test/sea/test-single-executable-blob-config-errors.js similarity index 90% rename from test/parallel/test-single-executable-blob-config-errors.js rename to test/sea/test-single-executable-blob-config-errors.js index a30850010e2e4d..322f430aad81d6 100644 --- a/test/parallel/test-single-executable-blob-config-errors.js +++ b/test/sea/test-single-executable-blob-config-errors.js @@ -16,7 +16,7 @@ const { spawnSyncAndAssert } = require('../common/child_process'); cwd: tmpdir.path, }, { status: 1, - stderr: /Cannot read single executable configuration from non-existent-relative\.json/ + stderr: /Cannot read single executable configuration from non-existent-relative\.json/, }); } @@ -29,7 +29,7 @@ const { spawnSyncAndAssert } = require('../common/child_process'); cwd: tmpdir.path, }, { status: 1, - stderr: /Cannot read single executable configuration from .*non-existent-absolute\.json/ + stderr: /Cannot read single executable configuration from .*non-existent-absolute\.json/, }); } @@ -43,7 +43,7 @@ const { spawnSyncAndAssert } = require('../common/child_process'); cwd: tmpdir.path, }, { status: 1, - stderr: /INCOMPLETE_ARRAY_OR_OBJECT/ + stderr: /INCOMPLETE_ARRAY_OR_OBJECT/, }); } @@ -57,7 +57,7 @@ const { spawnSyncAndAssert } = require('../common/child_process'); cwd: tmpdir.path, }, { status: 1, - stderr: /"main" field of .*empty\.json is not a non-empty string/ + stderr: /"main" field of .*empty\.json is not a non-empty string/, }); } @@ -71,7 +71,7 @@ const { spawnSyncAndAssert } = require('../common/child_process'); cwd: tmpdir.path, }, { status: 1, - stderr: /"main" field of .*no-main\.json is not a non-empty string/ + stderr: /"main" field of .*no-main\.json is not a non-empty string/, }); } @@ -85,7 +85,7 @@ const { spawnSyncAndAssert } = require('../common/child_process'); cwd: tmpdir.path, }, { status: 1, - stderr: /"output" field of .*no-output\.json is not a non-empty string/ + stderr: /"output" field of .*no-output\.json is not a non-empty string/, }); } @@ -105,7 +105,7 @@ const { spawnSyncAndAssert } = require('../common/child_process'); cwd: tmpdir.path, }, { status: 1, - stderr: /"disableExperimentalSEAWarning" field of .*invalid-disableExperimentalSEAWarning\.json is not a Boolean/ + stderr: /"disableExperimentalSEAWarning" field of .*invalid-disableExperimentalSEAWarning\.json is not a Boolean/, }); } @@ -119,7 +119,7 @@ const { spawnSyncAndAssert } = require('../common/child_process'); cwd: tmpdir.path, }, { status: 1, - stderr: /Cannot read main script .*bundle\.js/ + stderr: /Cannot read main script .*bundle\.js/, }); } @@ -129,7 +129,7 @@ const { spawnSyncAndAssert } = require('../common/child_process'); const main = tmpdir.resolve('bundle.js'); const configJson = JSON.stringify({ main, - output: 'sea.blob' + output: 'sea.blob', }); writeFileSync(config, configJson, 'utf8'); spawnSyncAndAssert( @@ -138,7 +138,7 @@ const { spawnSyncAndAssert } = require('../common/child_process'); cwd: tmpdir.path, }, { status: 1, - stderr: /Cannot read main script .*bundle\.js/ + stderr: /Cannot read main script .*bundle\.js/, }); } @@ -160,7 +160,7 @@ const { spawnSyncAndAssert } = require('../common/child_process'); cwd: tmpdir.path, }, { status: 1, - stderr: /Cannot write output to .*output-dir/ + stderr: /Cannot write output to .*output-dir/, }); } @@ -173,7 +173,7 @@ const { spawnSyncAndAssert } = require('../common/child_process'); writeFileSync(main, 'console.log("hello")', 'utf-8'); const configJson = JSON.stringify({ main, - output: 'output-dir' + output: 'output-dir', }); writeFileSync(config, configJson, 'utf8'); spawnSyncAndAssert( @@ -182,6 +182,6 @@ const { spawnSyncAndAssert } = require('../common/child_process'); cwd: tmpdir.path, }, { status: 1, - stderr: /Cannot write output to output-dir/ + stderr: /Cannot write output to output-dir/, }); } diff --git a/test/parallel/test-single-executable-blob-config.js b/test/sea/test-single-executable-blob-config.js similarity index 99% rename from test/parallel/test-single-executable-blob-config.js rename to test/sea/test-single-executable-blob-config.js index 4d56bc35066715..f3a148ae5a5559 100644 --- a/test/parallel/test-single-executable-blob-config.js +++ b/test/sea/test-single-executable-blob-config.js @@ -35,7 +35,7 @@ const assert = require('assert'); writeFileSync(main, 'console.log("hello")', 'utf-8'); const configJson = JSON.stringify({ main: 'bundle.js', - output: 'output.blob' + output: 'output.blob', }); writeFileSync(config, configJson, 'utf8'); const child = spawnSync( diff --git a/test/sea/testcfg.py b/test/sea/testcfg.py new file mode 100644 index 00000000000000..341d23919334c2 --- /dev/null +++ b/test/sea/testcfg.py @@ -0,0 +1,6 @@ +import sys, os +sys.path.append(os.path.join(os.path.dirname(__file__), '..')) +import testpy + +def GetConfiguration(context, root): + return testpy.SimpleTestConfiguration(context, root, 'sea') diff --git a/test/sequential/sequential.status b/test/sequential/sequential.status index c36eac6302d43a..627e61911d15ca 100644 --- a/test/sequential/sequential.status +++ b/test/sequential/sequential.status @@ -14,9 +14,6 @@ test-watch-mode-inspect: PASS, FLAKY test-http2-large-file: PASS, FLAKY [$system==win32] -# https://github.com/nodejs/node/issues/49630 -test-single-executable-application-snapshot: PASS, FLAKY -test-single-executable-application-snapshot-and-code-cache: PASS, FLAKY # https://github.com/nodejs/node/issues/47409 test-http2-large-file: PASS, FLAKY @@ -26,27 +23,6 @@ test-http-server-request-timeouts-mixed: PASS, FLAKY [$system==macos] -[$system==macos && $arch==x64] -# https://github.com/nodejs/node/issues/59553 -test-single-executable-application: PASS, FLAKY -test-single-executable-application-assets: PASS, FLAKY -test-single-executable-application-assets-raw: PASS, FLAKY -test-single-executable-application-asset-keys-empty: PASS, FLAKY -test-single-executable-application-asset-keys: PASS, FLAKY -test-single-executable-application-disable-experimental-sea-warning: PASS, FLAKY -test-single-executable-application-empty: PASS, FLAKY -test-single-executable-application-exec-argv: PASS, FLAKY -test-single-executable-application-exec-argv-empty: PASS, FLAKY -test-single-executable-application-exec-argv-extension-cli: PASS, FLAKY -test-single-executable-application-exec-argv-extension-env: PASS, FLAKY -test-single-executable-application-exec-argv-extension-none: PASS, FLAKY -test-single-executable-application-inspect-in-sea-flags: PASS, FLAKY -test-single-executable-application-inspect: PASS, FLAKY -test-single-executable-application-snapshot: PASS, FLAKY -test-single-executable-application-snapshot-and-code-cache: PASS, FLAKY -test-single-executable-application-snapshot-worker: PASS, FLAKY -test-single-executable-application-use-code-cache: PASS, FLAKY - # https://github.com/nodejs/node/issues/43465 test-http-server-request-timeouts-mixed: PASS, FLAKY @@ -70,24 +46,3 @@ test-tls-psk-client: PASS, FLAKY [$arch==arm] # https://github.com/nodejs/node/issues/49933 test-watch-mode-inspect: SKIP - -[$system==linux && $arch==ppc64] -# https://github.com/nodejs/node/issues/59561 -test-single-executable-application: SKIP -test-single-executable-application-assets: SKIP -test-single-executable-application-assets-raw: SKIP -test-single-executable-application-asset-keys-empty: SKIP -test-single-executable-application-asset-keys: SKIP -test-single-executable-application-disable-experimental-sea-warning: SKIP -test-single-executable-application-empty: SKIP -test-single-executable-application-exec-argv: SKIP -test-single-executable-application-exec-argv-empty: SKIP -test-single-executable-application-exec-argv-extension-cli: SKIP -test-single-executable-application-exec-argv-extension-env: SKIP -test-single-executable-application-exec-argv-extension-none: SKIP -test-single-executable-application-inspect-in-sea-flags: SKIP -test-single-executable-application-inspect: SKIP -test-single-executable-application-snapshot: SKIP -test-single-executable-application-snapshot-and-code-cache: SKIP -test-single-executable-application-snapshot-worker: SKIP -test-single-executable-application-use-code-cache: SKIP diff --git a/test/sequential/test-perf-hooks.js b/test/sequential/test-perf-hooks.js index 847decdef18bfc..2db7353cf5fc93 100644 --- a/test/sequential/test-perf-hooks.js +++ b/test/sequential/test-perf-hooks.js @@ -28,15 +28,10 @@ const epsilon = 50; { const uptime1 = Date.now() - performance.timeOrigin; const uptime2 = performance.now(); - const uptime3 = process.uptime() * 1000; assert(Math.abs(uptime1 - uptime2) < epsilon, `Date.now() - performance.timeOrigin (${uptime1}) - ` + `performance.now() (${uptime2}) = ` + `${uptime1 - uptime2} >= +- ${epsilon}`); - assert(Math.abs(uptime1 - uptime3) < epsilon, - `Date.now() - performance.timeOrigin (${uptime1}) - ` + - `process.uptime() * 1000 (${uptime3}) = ` + - `${uptime1 - uptime3} >= +- ${epsilon}`); } assert.strictEqual(performance.nodeTiming.name, 'node'); @@ -58,8 +53,8 @@ function checkNodeTiming(timing) { // performance.now() i.e. measures Node.js instance up time. assert.strictEqual(typeof timing.duration, 'number'); assert(timing.duration > 0, `timing.duration ${timing.duration} <= 0`); - assert(delta < 10, - `now (${now}) - timing.duration (${timing.duration}) = ${delta} >= ${10}`); + assert(delta < epsilon, + `now (${now}) - timing.duration (${timing.duration}) = ${delta} >= ${epsilon}`); // Check that the following fields do not change. assert.strictEqual(timing.startTime, initialTiming.startTime); @@ -93,6 +88,18 @@ checkNodeTiming(initialTiming); assert(nodeStart < testStartTime, `nodeStart ${nodeStart} >= ${testStartTime}`); + { + // Due to https://github.com/nodejs/node/pull/46588, + // The difference between process.uptime() and (Date.now() - performance.timeOrigin) + // is roughly performance.nodeTiming.nodeStart + const uptime1 = Date.now() - performance.timeOrigin; // now - process start time + const uptime3 = process.uptime() * 1000; // now - node start time + assert(Math.abs(uptime1 - uptime3 - nodeStart) < epsilon, + `Date.now() - performance.timeOrigin (${uptime1}) - ` + + `process.uptime() * 1000 (${uptime3}) = ` + + `${uptime1 - uptime3} >= +- ${epsilon}`); + } + assert.strictEqual(typeof v8Start, 'number'); assert(v8Start > 0, `v8Start ${v8Start} <= 0`); // V8 starts after the process starts. diff --git a/test/sequential/test-watch-mode.mjs b/test/sequential/test-watch-mode.mjs index f0a7d62e560821..b0318f3505c66a 100644 --- a/test/sequential/test-watch-mode.mjs +++ b/test/sequential/test-watch-mode.mjs @@ -791,4 +791,73 @@ process.on('message', (message) => { `Completed running ${inspect(file)}. Waiting for file changes before restarting...`, ]); }); + + it('should watch changes to a file from config file', async () => { + const file = createTmpFile(); + const configFile = createTmpFile(JSON.stringify({ watch: { 'watch': true } }), '.json'); + const { stderr, stdout } = await runWriteSucceed({ + file, watchedFile: file, args: ['--experimental-config-file', configFile, file], options: { + timeout: 10000 + } + }); + + assert.strictEqual(stderr, ''); + assert.deepStrictEqual(stdout, [ + 'running', + `Completed running ${inspect(file)}. Waiting for file changes before restarting...`, + `Restarting ${inspect(file)}`, + 'running', + `Completed running ${inspect(file)}. Waiting for file changes before restarting...`, + ]); + }); + + it('should watch changes to a file with watch-path from config file', { + skip: !supportsRecursive, + }, async () => { + const dir = tmpdir.resolve('subdir4'); + mkdirSync(dir); + const file = createTmpFile(); + const watchedFile = createTmpFile('', '.js', dir); + const configFile = createTmpFile(JSON.stringify({ watch: { 'watch-path': [dir] } }), '.json', dir); + + const args = ['--experimental-config-file', configFile, file]; + const { stderr, stdout } = await runWriteSucceed({ file, watchedFile, args }); + + assert.strictEqual(stderr, ''); + assert.deepStrictEqual(stdout, [ + 'running', + `Completed running ${inspect(file)}. Waiting for file changes before restarting...`, + `Restarting ${inspect(file)}`, + 'running', + `Completed running ${inspect(file)}. Waiting for file changes before restarting...`, + ]); + assert.strictEqual(stderr, ''); + }); + + it('should watch changes to a file from default config file', async () => { + const dir = tmpdir.resolve('subdir5'); + mkdirSync(dir); + + const file = createTmpFile('console.log("running");', '.js', dir); + writeFileSync(path.join(dir, 'node.config.json'), JSON.stringify({ watch: { 'watch': true } })); + + const { stderr, stdout } = await runWriteSucceed({ + file, + watchedFile: file, + args: ['--experimental-default-config-file', file], + options: { + timeout: 10000, + cwd: dir + } + }); + + assert.strictEqual(stderr, ''); + assert.deepStrictEqual(stdout, [ + 'running', + `Completed running ${inspect(file)}. Waiting for file changes before restarting...`, + `Restarting ${inspect(file)}`, + 'running', + `Completed running ${inspect(file)}. Waiting for file changes before restarting...`, + ]); + }); }); diff --git a/test/sequential/test-without-async-context-frame.mjs b/test/sequential/test-without-async-context-frame.mjs new file mode 100644 index 00000000000000..087bb67b71af78 --- /dev/null +++ b/test/sequential/test-without-async-context-frame.mjs @@ -0,0 +1,24 @@ +import { isWindows } from '../common/index.mjs'; +import { spawn } from 'node:child_process'; +import { once } from 'node:events'; +import { fileURLToPath } from 'node:url'; +import { strictEqual } from 'node:assert'; + +const python = process.env.PYTHON || (isWindows ? 'python' : 'python3'); + +const testRunner = fileURLToPath( + new URL('../../tools/test.py', import.meta.url) +); + +const proc = spawn(python, [ + testRunner, + `--mode=${process.features.debug ? 'debug' : 'release'}`, + `--shell=${process.execPath}`, + '--node-args=--no-async-context-frame', + '*/test-async-local-storage-*', +], { + stdio: ['inherit', 'inherit', 'inherit'], +}); + +const [code] = await once(proc, 'exit'); +strictEqual(code, 0); diff --git a/test/sequential/test-worker-prof.js b/test/sequential/test-worker-prof.js index 69d4b9f590e290..ab373fa85bc239 100644 --- a/test/sequential/test-worker-prof.js +++ b/test/sequential/test-worker-prof.js @@ -77,7 +77,7 @@ if (process.argv[2] === 'child') { // When not tracking Worker threads, only 1 or 2 ticks would // have been recorded. // prof_sampling_interval is by default 1 millisecond. A higher SPIN_MS - // should result in more ticks, while 15 should be safe on most machines. - assert(workerTicks.length > 15, `worker ticks <= 15:\n${workerTicks.join('\n')}`); - assert(parentTicks.length > 15, `parent ticks <= 15:\n${parentTicks.join('\n')}`); + // should result in more ticks, while 10 should be safe on most machines. + assert(workerTicks.length > 10, `worker ticks <= 10:\n${workerTicks.join('\n')}`); + assert(parentTicks.length > 10, `parent ticks <= 10:\n${parentTicks.join('\n')}`); } diff --git a/test/test-runner/test-output-abort-hooks.mjs b/test/test-runner/test-output-abort-hooks.mjs new file mode 100644 index 00000000000000..2bb1898c5cc7bd --- /dev/null +++ b/test/test-runner/test-output-abort-hooks.mjs @@ -0,0 +1,11 @@ +// Test that the output of test-runner/output/abort_hooks.js matches test-runner/output/abort_hooks.snapshot +import '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, defaultTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/abort_hooks.js'), + defaultTransform, + { flags: ['--test-reporter=tap'] }, +); diff --git a/test/test-runner/test-output-abort-runs-after-hook.mjs b/test/test-runner/test-output-abort-runs-after-hook.mjs new file mode 100644 index 00000000000000..e8716bb08333f7 --- /dev/null +++ b/test/test-runner/test-output-abort-runs-after-hook.mjs @@ -0,0 +1,12 @@ +// Test that the output of test-runner/output/abort-runs-after-hook.js matches +// test-runner/output/abort-runs-after-hook.snapshot +import '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, defaultTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/abort-runs-after-hook.js'), + defaultTransform, + { flags: ['--test-reporter=tap'] }, +); diff --git a/test/test-runner/test-output-abort-suite.mjs b/test/test-runner/test-output-abort-suite.mjs new file mode 100644 index 00000000000000..c3f1b4625b4f5f --- /dev/null +++ b/test/test-runner/test-output-abort-suite.mjs @@ -0,0 +1,11 @@ +// Test that the output of test-runner/output/abort_suite.js matches test-runner/output/abort_suite.snapshot +import '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, defaultTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/abort_suite.js'), + defaultTransform, + { flags: ['--test-reporter=tap'] }, +); diff --git a/test/test-runner/test-output-abort.mjs b/test/test-runner/test-output-abort.mjs new file mode 100644 index 00000000000000..b361bc68035824 --- /dev/null +++ b/test/test-runner/test-output-abort.mjs @@ -0,0 +1,11 @@ +// Test that the output of test-runner/output/abort.js matches test-runner/output/abort.snapshot +import '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, defaultTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/abort.js'), + defaultTransform, + { flags: ['--test-reporter=tap'] }, +); diff --git a/test/test-runner/test-output-arbitrary-output-colored.mjs b/test/test-runner/test-output-arbitrary-output-colored.mjs new file mode 100644 index 00000000000000..77a13d8890430e --- /dev/null +++ b/test/test-runner/test-output-arbitrary-output-colored.mjs @@ -0,0 +1,27 @@ +// Test that the output of test-runner/output/arbitrary-output-colored.js matches +// test-runner/output/arbitrary-output-colored.snapshot +import * as common from '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { + spawnAndAssert, + specTransform, + replaceTestDuration, + transform, + ensureCwdIsProjectRoot, +} from '../common/assertSnapshot.js'; + +const skipForceColors = + process.config.variables.icu_gyp_path !== 'tools/icu/icu-generic.gyp' || + process.config.variables.node_shared_openssl; + +if (skipForceColors) { + // https://github.com/nodejs/node/pull/48057 + common.skip('Forced colors not supported in this build'); +} + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/arbitrary-output-colored.js'), + transform(specTransform, replaceTestDuration), + { tty: true }, +); diff --git a/test/test-runner/test-output-arbitrary-output.mjs b/test/test-runner/test-output-arbitrary-output.mjs new file mode 100644 index 00000000000000..d7626bb01856fa --- /dev/null +++ b/test/test-runner/test-output-arbitrary-output.mjs @@ -0,0 +1,12 @@ +// Test that the output of test-runner/output/arbitrary-output.js matches +// test-runner/output/arbitrary-output.snapshot +import '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, defaultTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/arbitrary-output.js'), + defaultTransform, + { flags: ['--test-reporter=tap'] }, +); diff --git a/test/test-runner/test-output-assertion-color-tty.mjs b/test/test-runner/test-output-assertion-color-tty.mjs new file mode 100644 index 00000000000000..102b397c815746 --- /dev/null +++ b/test/test-runner/test-output-assertion-color-tty.mjs @@ -0,0 +1,17 @@ +// Flags: --expose-internals +// Test that the output of test-runner/output/assertion-color-tty.mjs matches +// test-runner/output/assertion-color-tty.snapshot +import * as common from '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, specTransform, canColorize, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +if (!canColorize()) { + common.skip('TTY colors not supported'); +} + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/assertion-color-tty.mjs'), + specTransform, + { flags: ['--test', '--stack-trace-limit=0'], tty: true }, +); diff --git a/test/test-runner/test-output-async-test-scheduling.mjs b/test/test-runner/test-output-async-test-scheduling.mjs new file mode 100644 index 00000000000000..5903619f7e1266 --- /dev/null +++ b/test/test-runner/test-output-async-test-scheduling.mjs @@ -0,0 +1,12 @@ +// Test that the output of test-runner/output/async-test-scheduling.mjs matches +// test-runner/output/async-test-scheduling.snapshot +import '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, defaultTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/async-test-scheduling.mjs'), + defaultTransform, + { flags: ['--test-reporter=tap'] }, +); diff --git a/test/test-runner/test-output-before-and-after-each-too-many-listeners.mjs b/test/test-runner/test-output-before-and-after-each-too-many-listeners.mjs new file mode 100644 index 00000000000000..bcaec70b17276a --- /dev/null +++ b/test/test-runner/test-output-before-and-after-each-too-many-listeners.mjs @@ -0,0 +1,12 @@ +// Test that the output of test-runner/output/before-and-after-each-too-many-listeners.js matches +// test-runner/output/before-and-after-each-too-many-listeners.snapshot +import '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, defaultTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/before-and-after-each-too-many-listeners.js'), + defaultTransform, + { flags: ['--test-reporter=tap'] }, +); diff --git a/test/test-runner/test-output-before-and-after-each-with-timeout-too-many-listeners.mjs b/test/test-runner/test-output-before-and-after-each-with-timeout-too-many-listeners.mjs new file mode 100644 index 00000000000000..7804692911aeda --- /dev/null +++ b/test/test-runner/test-output-before-and-after-each-with-timeout-too-many-listeners.mjs @@ -0,0 +1,12 @@ +// Test that the output of test-runner/output/before-and-after-each-with-timeout-too-many-listeners.js matches +// test-runner/output/before-and-after-each-with-timeout-too-many-listeners.snapshot +import '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, defaultTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/before-and-after-each-with-timeout-too-many-listeners.js'), + defaultTransform, + { flags: ['--test-reporter=tap'] }, +); diff --git a/test/test-runner/test-output-coverage-failure.mjs b/test/test-runner/test-output-coverage-failure.mjs new file mode 100644 index 00000000000000..fc171db3ef4627 --- /dev/null +++ b/test/test-runner/test-output-coverage-failure.mjs @@ -0,0 +1,16 @@ +// Test that the output of test-runner/output/coverage_failure.js matches +// test-runner/output/coverage_failure.snapshot +import * as common from '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, defaultTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +if (!process.features.inspector) { + common.skip('inspector support required'); +} + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/coverage_failure.js'), + defaultTransform, + { flags: ['--test-reporter=tap', '--test-coverage-exclude=!test/**'] }, +); diff --git a/test/test-runner/test-output-coverage-short-filename.mjs b/test/test-runner/test-output-coverage-short-filename.mjs new file mode 100644 index 00000000000000..04d673f530689f --- /dev/null +++ b/test/test-runner/test-output-coverage-short-filename.mjs @@ -0,0 +1,19 @@ +// Test that the output of test-runner/output/coverage-short-filename.mjs matches +// test-runner/output/coverage-short-filename.snapshot +import * as common from '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, defaultTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +if (!process.features.inspector) { + common.skip('inspector support required'); +} + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/coverage-short-filename.mjs'), + defaultTransform, + { + flags: ['--test-reporter=tap', '--test-coverage-exclude=../output/**'], + cwd: fixtures.path('test-runner/coverage-snap'), + }, +); diff --git a/test/test-runner/test-output-coverage-width-100-uncovered-lines.mjs b/test/test-runner/test-output-coverage-width-100-uncovered-lines.mjs new file mode 100644 index 00000000000000..dd7e1a9c455fbe --- /dev/null +++ b/test/test-runner/test-output-coverage-width-100-uncovered-lines.mjs @@ -0,0 +1,16 @@ +// Test that the output of test-runner/output/coverage-width-100-uncovered-lines.mjs matches +// test-runner/output/coverage-width-100-uncovered-lines.snapshot +import * as common from '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, defaultTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +if (!process.features.inspector) { + common.skip('inspector support required'); +} + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/coverage-width-100-uncovered-lines.mjs'), + defaultTransform, + { flags: ['--test-reporter=tap', '--test-coverage-exclude=!test/**'] }, +); diff --git a/test/test-runner/test-output-coverage-width-100.mjs b/test/test-runner/test-output-coverage-width-100.mjs new file mode 100644 index 00000000000000..515e1d4e02cac4 --- /dev/null +++ b/test/test-runner/test-output-coverage-width-100.mjs @@ -0,0 +1,16 @@ +// Test that the output of test-runner/output/coverage-width-100.mjs matches +// test-runner/output/coverage-width-100.snapshot +import * as common from '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, defaultTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +if (!process.features.inspector) { + common.skip('inspector support required'); +} + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/coverage-width-100.mjs'), + defaultTransform, + { flags: ['--test-reporter=tap', '--test-coverage-exclude=!test/**'] }, +); diff --git a/test/test-runner/test-output-coverage-width-150-uncovered-lines.mjs b/test/test-runner/test-output-coverage-width-150-uncovered-lines.mjs new file mode 100644 index 00000000000000..a09aa303c8cc73 --- /dev/null +++ b/test/test-runner/test-output-coverage-width-150-uncovered-lines.mjs @@ -0,0 +1,16 @@ +// Test that the output of test-runner/output/coverage-width-150-uncovered-lines.mjs matches +// test-runner/output/coverage-width-150-uncovered-lines.snapshot +import * as common from '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, defaultTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +if (!process.features.inspector) { + common.skip('inspector support required'); +} + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/coverage-width-150-uncovered-lines.mjs'), + defaultTransform, + { flags: ['--test-reporter=tap', '--test-coverage-exclude=!test/**'] }, +); diff --git a/test/test-runner/test-output-coverage-width-150.mjs b/test/test-runner/test-output-coverage-width-150.mjs new file mode 100644 index 00000000000000..077483de649f11 --- /dev/null +++ b/test/test-runner/test-output-coverage-width-150.mjs @@ -0,0 +1,16 @@ +// Test that the output of test-runner/output/coverage-width-150.mjs matches +// test-runner/output/coverage-width-150.snapshot +import * as common from '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, defaultTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +if (!process.features.inspector) { + common.skip('inspector support required'); +} + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/coverage-width-150.mjs'), + defaultTransform, + { flags: ['--test-reporter=tap', '--test-coverage-exclude=!test/**'] }, +); diff --git a/test/test-runner/test-output-coverage-width-40.mjs b/test/test-runner/test-output-coverage-width-40.mjs new file mode 100644 index 00000000000000..2fa862ac48e65b --- /dev/null +++ b/test/test-runner/test-output-coverage-width-40.mjs @@ -0,0 +1,16 @@ +// Test that the output of test-runner/output/coverage-width-40.mjs matches +// test-runner/output/coverage-width-40.snapshot +import * as common from '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, defaultTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +if (!process.features.inspector) { + common.skip('inspector support required'); +} + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/coverage-width-40.mjs'), + defaultTransform, + { flags: ['--test-reporter=tap', '--test-coverage-exclude=!test/**'] }, +); diff --git a/test/test-runner/test-output-coverage-width-80-color.mjs b/test/test-runner/test-output-coverage-width-80-color.mjs new file mode 100644 index 00000000000000..97d11c2a48a584 --- /dev/null +++ b/test/test-runner/test-output-coverage-width-80-color.mjs @@ -0,0 +1,21 @@ +// Flags: --expose-internals +// Test that the output of test-runner/output/coverage-width-80-color.mjs matches +// test-runner/output/coverage-width-80-color.snapshot +import * as common from '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, specTransform, canColorize, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +if (!process.features.inspector) { + common.skip('inspector support required'); +} + +if (!canColorize()) { + common.skip('TTY colors not supported'); +} + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/coverage-width-80-color.mjs'), + specTransform, + { flags: ['--test-coverage-exclude=!test/**'], tty: true }, +); diff --git a/test/test-runner/test-output-coverage-width-80-uncovered-lines-color.mjs b/test/test-runner/test-output-coverage-width-80-uncovered-lines-color.mjs new file mode 100644 index 00000000000000..1b2cb29620d6ff --- /dev/null +++ b/test/test-runner/test-output-coverage-width-80-uncovered-lines-color.mjs @@ -0,0 +1,21 @@ +// Flags: --expose-internals +// Test that the output of test-runner/output/coverage-width-80-uncovered-lines-color.mjs matches +// test-runner/output/coverage-width-80-uncovered-lines-color.snapshot +import * as common from '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, specTransform, canColorize, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +if (!process.features.inspector) { + common.skip('inspector support required'); +} + +if (!canColorize()) { + common.skip('TTY colors not supported'); +} + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/coverage-width-80-uncovered-lines-color.mjs'), + specTransform, + { flags: ['--test-coverage-exclude=!test/**'], tty: true }, +); diff --git a/test/test-runner/test-output-coverage-width-80-uncovered-lines.mjs b/test/test-runner/test-output-coverage-width-80-uncovered-lines.mjs new file mode 100644 index 00000000000000..39cc7d9403c96a --- /dev/null +++ b/test/test-runner/test-output-coverage-width-80-uncovered-lines.mjs @@ -0,0 +1,16 @@ +// Test that the output of test-runner/output/coverage-width-80-uncovered-lines.mjs matches +// test-runner/output/coverage-width-80-uncovered-lines.snapshot +import * as common from '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, defaultTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +if (!process.features.inspector) { + common.skip('inspector support required'); +} + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/coverage-width-80-uncovered-lines.mjs'), + defaultTransform, + { flags: ['--test-reporter=tap', '--test-coverage-exclude=!test/**'] }, +); diff --git a/test/test-runner/test-output-coverage-width-80.mjs b/test/test-runner/test-output-coverage-width-80.mjs new file mode 100644 index 00000000000000..dabf7644726b43 --- /dev/null +++ b/test/test-runner/test-output-coverage-width-80.mjs @@ -0,0 +1,16 @@ +// Test that the output of test-runner/output/coverage-width-80.mjs matches +// test-runner/output/coverage-width-80.snapshot +import * as common from '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, defaultTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +if (!process.features.inspector) { + common.skip('inspector support required'); +} + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/coverage-width-80.mjs'), + defaultTransform, + { flags: ['--test-reporter=tap', '--test-coverage-exclude=!test/**'] }, +); diff --git a/test/test-runner/test-output-coverage-width-infinity-uncovered-lines.mjs b/test/test-runner/test-output-coverage-width-infinity-uncovered-lines.mjs new file mode 100644 index 00000000000000..b75bec1e04ec37 --- /dev/null +++ b/test/test-runner/test-output-coverage-width-infinity-uncovered-lines.mjs @@ -0,0 +1,16 @@ +// Test that the output of test-runner/output/coverage-width-infinity-uncovered-lines.mjs matches +// test-runner/output/coverage-width-infinity-uncovered-lines.snapshot +import * as common from '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, defaultTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +if (!process.features.inspector) { + common.skip('inspector support required'); +} + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/coverage-width-infinity-uncovered-lines.mjs'), + defaultTransform, + { flags: ['--test-reporter=tap', '--test-coverage-exclude=!test/**'] }, +); diff --git a/test/test-runner/test-output-coverage-width-infinity.mjs b/test/test-runner/test-output-coverage-width-infinity.mjs new file mode 100644 index 00000000000000..f586ff9ab159d0 --- /dev/null +++ b/test/test-runner/test-output-coverage-width-infinity.mjs @@ -0,0 +1,16 @@ +// Test that the output of test-runner/output/coverage-width-infinity.mjs matches +// test-runner/output/coverage-width-infinity.snapshot +import * as common from '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, defaultTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +if (!process.features.inspector) { + common.skip('inspector support required'); +} + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/coverage-width-infinity.mjs'), + defaultTransform, + { flags: ['--test-reporter=tap', '--test-coverage-exclude=!test/**'] }, +); diff --git a/test/test-runner/test-output-coverage-with-mock.mjs b/test/test-runner/test-output-coverage-with-mock.mjs new file mode 100644 index 00000000000000..f489451e184d6d --- /dev/null +++ b/test/test-runner/test-output-coverage-with-mock.mjs @@ -0,0 +1,25 @@ +// Test that the output of test-runner/output/coverage-with-mock.mjs matches +// test-runner/output/coverage-with-mock.snapshot +import * as common from '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, defaultTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +if (!process.features.inspector) { + common.skip('inspector support required'); +} + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/coverage-with-mock.mjs'), + defaultTransform, + { + flags: [ + '--disable-warning=ExperimentalWarning', + '--test-reporter=tap', + '--experimental-transform-types', + '--experimental-test-module-mocks', + '--experimental-test-coverage', + '--test-coverage-exclude=!test/**', + ], + }, +); diff --git a/test/test-runner/test-output-default-output.mjs b/test/test-runner/test-output-default-output.mjs new file mode 100644 index 00000000000000..96ae139902755e --- /dev/null +++ b/test/test-runner/test-output-default-output.mjs @@ -0,0 +1,11 @@ +// Test that the output of test-runner/output/default_output.js matches test-runner/output/default_output.snapshot +import '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, specTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/default_output.js'), + specTransform, + { tty: true }, +); diff --git a/test/test-runner/test-output-describe-it.mjs b/test/test-runner/test-output-describe-it.mjs new file mode 100644 index 00000000000000..219951d5aec75e --- /dev/null +++ b/test/test-runner/test-output-describe-it.mjs @@ -0,0 +1,11 @@ +// Test that the output of test-runner/output/describe_it.js matches test-runner/output/describe_it.snapshot +import '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, defaultTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/describe_it.js'), + defaultTransform, + { flags: ['--test-reporter=tap'] }, +); diff --git a/test/test-runner/test-output-describe-nested.mjs b/test/test-runner/test-output-describe-nested.mjs new file mode 100644 index 00000000000000..661048527fa668 --- /dev/null +++ b/test/test-runner/test-output-describe-nested.mjs @@ -0,0 +1,11 @@ +// Test that the output of test-runner/output/describe_nested.js matches test-runner/output/describe_nested.snapshot +import '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, defaultTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/describe_nested.js'), + defaultTransform, + { flags: ['--test-reporter=tap'] }, +); diff --git a/test/test-runner/test-output-dot-output-custom-columns.mjs b/test/test-runner/test-output-dot-output-custom-columns.mjs new file mode 100644 index 00000000000000..c8061e6ba27e85 --- /dev/null +++ b/test/test-runner/test-output-dot-output-custom-columns.mjs @@ -0,0 +1,12 @@ +// Test that the output of test-runner/output/dot_output_custom_columns.js matches +// test-runner/output/dot_output_custom_columns.snapshot +import '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, specTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/dot_output_custom_columns.js'), + specTransform, + { tty: true }, +); diff --git a/test/test-runner/test-output-dot-reporter.mjs b/test/test-runner/test-output-dot-reporter.mjs new file mode 100644 index 00000000000000..46312203bb46d7 --- /dev/null +++ b/test/test-runner/test-output-dot-reporter.mjs @@ -0,0 +1,10 @@ +// Test that the output of test-runner/output/dot_reporter.js matches test-runner/output/dot_reporter.snapshot +import '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, specTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/dot_reporter.js'), + specTransform, +); diff --git a/test/test-runner/test-output-eval-dot.mjs b/test/test-runner/test-output-eval-dot.mjs new file mode 100644 index 00000000000000..e31334ac52dd2b --- /dev/null +++ b/test/test-runner/test-output-eval-dot.mjs @@ -0,0 +1,10 @@ +// Test that the output of test-runner/output/eval_dot.js matches test-runner/output/eval_dot.snapshot +import '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, specTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/eval_dot.js'), + specTransform, +); diff --git a/test/test-runner/test-output-eval-spec.mjs b/test/test-runner/test-output-eval-spec.mjs new file mode 100644 index 00000000000000..86d6677552bdc5 --- /dev/null +++ b/test/test-runner/test-output-eval-spec.mjs @@ -0,0 +1,10 @@ +// Test that the output of test-runner/output/eval_spec.js matches test-runner/output/eval_spec.snapshot +import '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, specTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/eval_spec.js'), + specTransform, +); diff --git a/test/test-runner/test-output-eval-tap.mjs b/test/test-runner/test-output-eval-tap.mjs new file mode 100644 index 00000000000000..a1902ec92878f4 --- /dev/null +++ b/test/test-runner/test-output-eval-tap.mjs @@ -0,0 +1,10 @@ +// Test that the output of test-runner/output/eval_tap.js matches test-runner/output/eval_tap.snapshot +import '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, defaultTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/eval_tap.js'), + defaultTransform, +); diff --git a/test/test-runner/test-output-filtered-suite-delayed-build.mjs b/test/test-runner/test-output-filtered-suite-delayed-build.mjs new file mode 100644 index 00000000000000..c4ec3f3b019731 --- /dev/null +++ b/test/test-runner/test-output-filtered-suite-delayed-build.mjs @@ -0,0 +1,12 @@ +// Test that the output of test-runner/output/filtered-suite-delayed-build.js matches +// test-runner/output/filtered-suite-delayed-build.snapshot +import '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, defaultTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/filtered-suite-delayed-build.js'), + defaultTransform, + { flags: ['--test-reporter=tap'] }, +); diff --git a/test/test-runner/test-output-filtered-suite-order.mjs b/test/test-runner/test-output-filtered-suite-order.mjs new file mode 100644 index 00000000000000..f29c4dcf62bc3f --- /dev/null +++ b/test/test-runner/test-output-filtered-suite-order.mjs @@ -0,0 +1,12 @@ +// Test that the output of test-runner/output/filtered-suite-order.mjs matches +// test-runner/output/filtered-suite-order.snapshot +import '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, defaultTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/filtered-suite-order.mjs'), + defaultTransform, + { flags: ['--test-reporter=tap'] }, +); diff --git a/test/test-runner/test-output-filtered-suite-throws.mjs b/test/test-runner/test-output-filtered-suite-throws.mjs new file mode 100644 index 00000000000000..7976e3b63753de --- /dev/null +++ b/test/test-runner/test-output-filtered-suite-throws.mjs @@ -0,0 +1,12 @@ +// Test that the output of test-runner/output/filtered-suite-throws.js matches +// test-runner/output/filtered-suite-throws.snapshot +import '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, defaultTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/filtered-suite-throws.js'), + defaultTransform, + { flags: ['--test-reporter=tap'] }, +); diff --git a/test/test-runner/test-output-force-exit.mjs b/test/test-runner/test-output-force-exit.mjs new file mode 100644 index 00000000000000..5138e3df57da31 --- /dev/null +++ b/test/test-runner/test-output-force-exit.mjs @@ -0,0 +1,10 @@ +// Test that the output of test-runner/output/force_exit.js matches test-runner/output/force_exit.snapshot +import '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, specTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/force_exit.js'), + specTransform, +); diff --git a/test/test-runner/test-output-global-after-should-fail-the-test.mjs b/test/test-runner/test-output-global-after-should-fail-the-test.mjs new file mode 100644 index 00000000000000..71179d7f15ebfc --- /dev/null +++ b/test/test-runner/test-output-global-after-should-fail-the-test.mjs @@ -0,0 +1,12 @@ +// Test that the output of test-runner/output/global_after_should_fail_the_test.js matches +// test-runner/output/global_after_should_fail_the_test.snapshot +import '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, defaultTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/global_after_should_fail_the_test.js'), + defaultTransform, + { flags: ['--test-reporter=tap'] }, +); diff --git a/test/test-runner/test-output-global-hooks-with-no-tests.mjs b/test/test-runner/test-output-global-hooks-with-no-tests.mjs new file mode 100644 index 00000000000000..361948df103ba2 --- /dev/null +++ b/test/test-runner/test-output-global-hooks-with-no-tests.mjs @@ -0,0 +1,12 @@ +// Test that the output of test-runner/output/global-hooks-with-no-tests.js matches +// test-runner/output/global-hooks-with-no-tests.snapshot +import '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, defaultTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/global-hooks-with-no-tests.js'), + defaultTransform, + { flags: ['--test-reporter=tap'] }, +); diff --git a/test/test-runner/test-output-hooks-spec-reporter.mjs b/test/test-runner/test-output-hooks-spec-reporter.mjs new file mode 100644 index 00000000000000..cc46592fb74c6d --- /dev/null +++ b/test/test-runner/test-output-hooks-spec-reporter.mjs @@ -0,0 +1,11 @@ +// Test that the output of test-runner/output/hooks_spec_reporter.js matches +// test-runner/output/hooks_spec_reporter.snapshot +import '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, specTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/hooks_spec_reporter.js'), + specTransform, +); diff --git a/test/test-runner/test-output-hooks-with-no-global-test.mjs b/test/test-runner/test-output-hooks-with-no-global-test.mjs new file mode 100644 index 00000000000000..a0c0327e0f9ec2 --- /dev/null +++ b/test/test-runner/test-output-hooks-with-no-global-test.mjs @@ -0,0 +1,12 @@ +// Test that the output of test-runner/output/hooks-with-no-global-test.js matches +// test-runner/output/hooks-with-no-global-test.snapshot +import '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, defaultTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/hooks-with-no-global-test.js'), + defaultTransform, + { flags: ['--test-reporter=tap'] }, +); diff --git a/test/test-runner/test-output-hooks.mjs b/test/test-runner/test-output-hooks.mjs new file mode 100644 index 00000000000000..b1e1d655df6db8 --- /dev/null +++ b/test/test-runner/test-output-hooks.mjs @@ -0,0 +1,11 @@ +// Test that the output of test-runner/output/hooks.js matches test-runner/output/hooks.snapshot +import '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, defaultTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/hooks.js'), + defaultTransform, + { flags: ['--test-reporter=tap'] }, +); diff --git a/test/test-runner/test-output-junit-reporter.mjs b/test/test-runner/test-output-junit-reporter.mjs new file mode 100644 index 00000000000000..ece14e09ac2e04 --- /dev/null +++ b/test/test-runner/test-output-junit-reporter.mjs @@ -0,0 +1,11 @@ +// Test that the output of test-runner/output/junit_reporter.js matches +// test-runner/output/junit_reporter.snapshot +import '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, junitTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/junit_reporter.js'), + junitTransform, +); diff --git a/test/test-runner/test-output-lcov-reporter.mjs b/test/test-runner/test-output-lcov-reporter.mjs new file mode 100644 index 00000000000000..93aa38fff2bc66 --- /dev/null +++ b/test/test-runner/test-output-lcov-reporter.mjs @@ -0,0 +1,14 @@ +// Test that the output of test-runner/output/lcov_reporter.js matches test-runner/output/lcov_reporter.snapshot +import * as common from '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, lcovTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +if (!process.features.inspector) { + common.skip('inspector support required'); +} + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/lcov_reporter.js'), + lcovTransform, +); diff --git a/test/test-runner/test-output-name-and-skip-patterns.mjs b/test/test-runner/test-output-name-and-skip-patterns.mjs new file mode 100644 index 00000000000000..875641a28435c4 --- /dev/null +++ b/test/test-runner/test-output-name-and-skip-patterns.mjs @@ -0,0 +1,12 @@ +// Test that the output of test-runner/output/name_and_skip_patterns.js matches +// test-runner/output/name_and_skip_patterns.snapshot +import '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, defaultTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/name_and_skip_patterns.js'), + defaultTransform, + { flags: ['--test-reporter=tap'] }, +); diff --git a/test/test-runner/test-output-name-pattern-with-only.mjs b/test/test-runner/test-output-name-pattern-with-only.mjs new file mode 100644 index 00000000000000..89b94f8a80a031 --- /dev/null +++ b/test/test-runner/test-output-name-pattern-with-only.mjs @@ -0,0 +1,12 @@ +// Test that the output of test-runner/output/name_pattern_with_only.js matches +// test-runner/output/name_pattern_with_only.snapshot +import '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, defaultTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/name_pattern_with_only.js'), + defaultTransform, + { flags: ['--test-reporter=tap'] }, +); diff --git a/test/test-runner/test-output-name-pattern.mjs b/test/test-runner/test-output-name-pattern.mjs new file mode 100644 index 00000000000000..efaef28ed0f0a9 --- /dev/null +++ b/test/test-runner/test-output-name-pattern.mjs @@ -0,0 +1,11 @@ +// Test that the output of test-runner/output/name_pattern.js matches test-runner/output/name_pattern.snapshot +import '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, defaultTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/name_pattern.js'), + defaultTransform, + { flags: ['--test-reporter=tap'] }, +); diff --git a/test/test-runner/test-output-no-refs.mjs b/test/test-runner/test-output-no-refs.mjs new file mode 100644 index 00000000000000..5e2fbab53b2f54 --- /dev/null +++ b/test/test-runner/test-output-no-refs.mjs @@ -0,0 +1,11 @@ +// Test that the output of test-runner/output/no_refs.js matches test-runner/output/no_refs.snapshot +import '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, defaultTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/no_refs.js'), + defaultTransform, + { flags: ['--test-reporter=tap'] }, +); diff --git a/test/test-runner/test-output-no-tests.mjs b/test/test-runner/test-output-no-tests.mjs new file mode 100644 index 00000000000000..0cff4003e7598c --- /dev/null +++ b/test/test-runner/test-output-no-tests.mjs @@ -0,0 +1,11 @@ +// Test that the output of test-runner/output/no_tests.js matches test-runner/output/no_tests.snapshot +import '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, defaultTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/no_tests.js'), + defaultTransform, + { flags: ['--test-reporter=tap'] }, +); diff --git a/test/test-runner/test-output-non-tty-forced-color-output.mjs b/test/test-runner/test-output-non-tty-forced-color-output.mjs new file mode 100644 index 00000000000000..d630c52e293a35 --- /dev/null +++ b/test/test-runner/test-output-non-tty-forced-color-output.mjs @@ -0,0 +1,11 @@ +// Test that the output of test-runner/output/non-tty-forced-color-output.js matches +// test-runner/output/non-tty-forced-color-output.snapshot +import '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, specTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/non-tty-forced-color-output.js'), + specTransform, +); diff --git a/test/test-runner/test-output-only-tests.mjs b/test/test-runner/test-output-only-tests.mjs new file mode 100644 index 00000000000000..93a9097bec15dc --- /dev/null +++ b/test/test-runner/test-output-only-tests.mjs @@ -0,0 +1,11 @@ +// Test that the output of test-runner/output/only_tests.js matches test-runner/output/only_tests.snapshot +import '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, defaultTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/only_tests.js'), + defaultTransform, + { flags: ['--test-reporter=tap'] }, +); diff --git a/test/test-runner/test-output-output-cli.mjs b/test/test-runner/test-output-output-cli.mjs new file mode 100644 index 00000000000000..924b6212e2eae8 --- /dev/null +++ b/test/test-runner/test-output-output-cli.mjs @@ -0,0 +1,10 @@ +// Test that the output of test-runner/output/output_cli.js matches test-runner/output/output_cli.snapshot +import '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, defaultTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/output_cli.js'), + defaultTransform, +); diff --git a/test/test-runner/test-output-output.mjs b/test/test-runner/test-output-output.mjs new file mode 100644 index 00000000000000..1eb152324c4063 --- /dev/null +++ b/test/test-runner/test-output-output.mjs @@ -0,0 +1,11 @@ +// Test that the output of test-runner/output/output.js matches test-runner/output/output.snapshot +import '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, defaultTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/output.js'), + defaultTransform, + { flags: ['--test-reporter=tap'] }, +); diff --git a/test/test-runner/test-output-skip-each-hooks.mjs b/test/test-runner/test-output-skip-each-hooks.mjs new file mode 100644 index 00000000000000..a5fb2fb613627b --- /dev/null +++ b/test/test-runner/test-output-skip-each-hooks.mjs @@ -0,0 +1,11 @@ +// Test that the output of test-runner/output/skip-each-hooks.js matches +// test-runner/output/skip-each-hooks.snapshot +import '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, specTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/skip-each-hooks.js'), + specTransform, +); diff --git a/test/test-runner/test-output-skip-pattern.mjs b/test/test-runner/test-output-skip-pattern.mjs new file mode 100644 index 00000000000000..852b48e7a2b6a1 --- /dev/null +++ b/test/test-runner/test-output-skip-pattern.mjs @@ -0,0 +1,11 @@ +// Test that the output of test-runner/output/skip_pattern.js matches test-runner/output/skip_pattern.snapshot +import '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, defaultTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/skip_pattern.js'), + defaultTransform, + { flags: ['--test-reporter=tap'] }, +); diff --git a/test/test-runner/test-output-source-mapped-locations.mjs b/test/test-runner/test-output-source-mapped-locations.mjs new file mode 100644 index 00000000000000..a24bed052dc3ad --- /dev/null +++ b/test/test-runner/test-output-source-mapped-locations.mjs @@ -0,0 +1,12 @@ +// Test that the output of test-runner/output/source_mapped_locations.mjs matches +// test-runner/output/source_mapped_locations.snapshot +import '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, defaultTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/source_mapped_locations.mjs'), + defaultTransform, + { flags: ['--test-reporter=tap'] }, +); diff --git a/test/test-runner/test-output-spec-reporter-cli.mjs b/test/test-runner/test-output-spec-reporter-cli.mjs new file mode 100644 index 00000000000000..5b44483601c3a5 --- /dev/null +++ b/test/test-runner/test-output-spec-reporter-cli.mjs @@ -0,0 +1,11 @@ +// Test that the output of test-runner/output/spec_reporter_cli.js matches +// test-runner/output/spec_reporter_cli.snapshot +import '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, specTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/spec_reporter_cli.js'), + specTransform, +); diff --git a/test/test-runner/test-output-spec-reporter-successful.mjs b/test/test-runner/test-output-spec-reporter-successful.mjs new file mode 100644 index 00000000000000..0308d1e1f01b24 --- /dev/null +++ b/test/test-runner/test-output-spec-reporter-successful.mjs @@ -0,0 +1,11 @@ +// Test that the output of test-runner/output/spec_reporter_successful.js matches +// test-runner/output/spec_reporter_successful.snapshot +import '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, specTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/spec_reporter_successful.js'), + specTransform, +); diff --git a/test/test-runner/test-output-spec-reporter.mjs b/test/test-runner/test-output-spec-reporter.mjs new file mode 100644 index 00000000000000..e7233b13da95d1 --- /dev/null +++ b/test/test-runner/test-output-spec-reporter.mjs @@ -0,0 +1,10 @@ +// Test that the output of test-runner/output/spec_reporter.js matches test-runner/output/spec_reporter.snapshot +import '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, specTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/spec_reporter.js'), + specTransform, +); diff --git a/test/test-runner/test-output-suite-skip-hooks.mjs b/test/test-runner/test-output-suite-skip-hooks.mjs new file mode 100644 index 00000000000000..183c4bb27c5d34 --- /dev/null +++ b/test/test-runner/test-output-suite-skip-hooks.mjs @@ -0,0 +1,11 @@ +// Test that the output of test-runner/output/suite-skip-hooks.js matches +// test-runner/output/suite-skip-hooks.snapshot +import '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, specTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/suite-skip-hooks.js'), + specTransform, +); diff --git a/test/test-runner/test-output-tap-escape.mjs b/test/test-runner/test-output-tap-escape.mjs new file mode 100644 index 00000000000000..f0b4471d5158bb --- /dev/null +++ b/test/test-runner/test-output-tap-escape.mjs @@ -0,0 +1,17 @@ +// Test that the output of test-runner/output/tap_escape.js matches test-runner/output/tap_escape.snapshot +import '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { + spawnAndAssert, + transform, + replaceWindowsLineEndings, + replaceTestDuration, + ensureCwdIsProjectRoot, +} from '../common/assertSnapshot.js'; + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/tap_escape.js'), + transform(replaceWindowsLineEndings, replaceTestDuration), + { flags: ['--test-reporter=tap'] }, +); diff --git a/test/test-runner/test-output-test-diagnostic-warning-without-test-only-flag.mjs b/test/test-runner/test-output-test-diagnostic-warning-without-test-only-flag.mjs new file mode 100644 index 00000000000000..b5f59df53b867d --- /dev/null +++ b/test/test-runner/test-output-test-diagnostic-warning-without-test-only-flag.mjs @@ -0,0 +1,12 @@ +// Test that the output of test-runner/output/test-diagnostic-warning-without-test-only-flag.js matches +// test-runner/output/test-diagnostic-warning-without-test-only-flag.snapshot +import '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, defaultTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/test-diagnostic-warning-without-test-only-flag.js'), + defaultTransform, + { flags: ['--test', '--test-reporter=tap'] }, +); diff --git a/test/test-runner/test-output-test-runner-plan-timeout.mjs b/test/test-runner/test-output-test-runner-plan-timeout.mjs new file mode 100644 index 00000000000000..6771468d3b9fa9 --- /dev/null +++ b/test/test-runner/test-output-test-runner-plan-timeout.mjs @@ -0,0 +1,12 @@ +// Test that the output of test-runner/output/test-runner-plan-timeout.js matches +// test-runner/output/test-runner-plan-timeout.snapshot +import '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, defaultTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/test-runner-plan-timeout.js'), + defaultTransform, + { flags: ['--test-reporter=tap', '--test-force-exit'] }, +); diff --git a/test/test-runner/test-output-test-runner-plan.mjs b/test/test-runner/test-output-test-runner-plan.mjs new file mode 100644 index 00000000000000..a4533e9a1b1196 --- /dev/null +++ b/test/test-runner/test-output-test-runner-plan.mjs @@ -0,0 +1,12 @@ +// Test that the output of test-runner/output/test-runner-plan.js matches +// test-runner/output/test-runner-plan.snapshot +import '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, defaultTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/test-runner-plan.js'), + defaultTransform, + { flags: ['--test-reporter=tap'] }, +); diff --git a/test/test-runner/test-output-test-runner-watch-spec.mjs b/test/test-runner/test-output-test-runner-watch-spec.mjs new file mode 100644 index 00000000000000..c835b11527a8f0 --- /dev/null +++ b/test/test-runner/test-output-test-runner-watch-spec.mjs @@ -0,0 +1,11 @@ +// Test that the output of test-runner/output/test-runner-watch-spec.mjs matches +// test-runner/output/test-runner-watch-spec.snapshot +import '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, specTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/test-runner-watch-spec.mjs'), + specTransform, +); diff --git a/test/test-runner/test-output-test-timeout-flag-with-test.mjs b/test/test-runner/test-output-test-timeout-flag-with-test.mjs new file mode 100644 index 00000000000000..f34db67d0be2c0 --- /dev/null +++ b/test/test-runner/test-output-test-timeout-flag-with-test.mjs @@ -0,0 +1,13 @@ +// Test that the output of test-runner/output/test-timeout-flag.js matches +// test-runner/output/test-timeout-flag.snapshot with --test flag. +// --test-timeout should work with or without --test flag. +import '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, defaultTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/test-timeout-flag.js'), + defaultTransform, + { flags: ['--test-reporter=tap', '--test-timeout=100', '--test'] }, +); diff --git a/test/test-runner/test-output-test-timeout-flag.mjs b/test/test-runner/test-output-test-timeout-flag.mjs new file mode 100644 index 00000000000000..96ef6c47af00aa --- /dev/null +++ b/test/test-runner/test-output-test-timeout-flag.mjs @@ -0,0 +1,12 @@ +// Test that the output of test-runner/output/test-timeout-flag.js matches +// test-runner/output/test-timeout-flag.snapshot +import '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, defaultTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/test-timeout-flag.js'), + defaultTransform, + { flags: ['--test-reporter=tap', '--test-timeout=100'] }, +); diff --git a/test/test-runner/test-output-timeout-in-before-each.mjs b/test/test-runner/test-output-timeout-in-before-each.mjs new file mode 100644 index 00000000000000..e0efd67155f556 --- /dev/null +++ b/test/test-runner/test-output-timeout-in-before-each.mjs @@ -0,0 +1,12 @@ +// Test that the output of test-runner/output/timeout_in_before_each_should_not_affect_further_tests.js matches +// test-runner/output/timeout_in_before_each_should_not_affect_further_tests.snapshot +import '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, defaultTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/timeout_in_before_each_should_not_affect_further_tests.js'), + defaultTransform, + { flags: ['--test-reporter=tap'] }, +); diff --git a/test/test-runner/test-output-typescript-coverage.mjs b/test/test-runner/test-output-typescript-coverage.mjs new file mode 100644 index 00000000000000..41aa97d194cc20 --- /dev/null +++ b/test/test-runner/test-output-typescript-coverage.mjs @@ -0,0 +1,25 @@ +// Test that the output of test-runner/output/typescript-coverage.mts matches +// test-runner/output/typescript-coverage.snapshot +import * as common from '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, defaultTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +if (!process.features.inspector) { + common.skip('inspector support required'); +} + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/typescript-coverage.mts'), + defaultTransform, + { + flags: [ + '--disable-warning=ExperimentalWarning', + '--test-reporter=tap', + '--experimental-transform-types', + '--experimental-test-module-mocks', + '--experimental-test-coverage', + '--test-coverage-exclude=!test/**', + ], + }, +); diff --git a/test/test-runner/test-output-unfinished-suite-async-error.mjs b/test/test-runner/test-output-unfinished-suite-async-error.mjs new file mode 100644 index 00000000000000..b6af0ae9a1170f --- /dev/null +++ b/test/test-runner/test-output-unfinished-suite-async-error.mjs @@ -0,0 +1,12 @@ +// Test that the output of test-runner/output/unfinished-suite-async-error.js matches +// test-runner/output/unfinished-suite-async-error.snapshot +import '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, defaultTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/unfinished-suite-async-error.js'), + defaultTransform, + { flags: ['--test-reporter=tap'] }, +); diff --git a/test/test-runner/test-runner.status b/test/test-runner/test-runner.status new file mode 100644 index 00000000000000..65ae96eb9e3726 --- /dev/null +++ b/test/test-runner/test-runner.status @@ -0,0 +1,7 @@ +prefix test-runner + +# To mark a test as flaky, list the test name in the appropriate section +# below, without ".js", followed by ": PASS,FLAKY". Example: +# sample-test : PASS,FLAKY + +[true] # This section applies to all platforms diff --git a/test/test-runner/testcfg.py b/test/test-runner/testcfg.py new file mode 100644 index 00000000000000..fa53433d0dd1e5 --- /dev/null +++ b/test/test-runner/testcfg.py @@ -0,0 +1,6 @@ +import sys, os +sys.path.append(os.path.join(os.path.dirname(__file__), '..')) +import testpy + +def GetConfiguration(context, root): + return testpy.ParallelTestConfiguration(context, root, 'test-runner') diff --git a/test/wpt/status/console.json b/test/wpt/status/console.json index 7c9dadf0cc4465..ba74911d66fdd2 100644 --- a/test/wpt/status/console.json +++ b/test/wpt/status/console.json @@ -1,7 +1,7 @@ { "idlharness.any.js": { "note": "https://github.com/nodejs/node/issues/44185", - "requires": ["crypto", "small-icu"] + "requires": ["crypto", "inspector", "small-icu"] }, "idlharness-shadowrealm.window.js": { "skip": "ShadowRealm support is not enabled" diff --git a/tools/certdata.txt b/tools/certdata.txt index aeedd56c53b172..1b9af7cd9adc2e 100644 --- a/tools/certdata.txt +++ b/tools/certdata.txt @@ -3195,7 +3195,6 @@ CKA_NSS_SERVER_DISTRUST_AFTER MULTILINE_OCTAL END CKA_NSS_EMAIL_DISTRUST_AFTER CK_BBOOL CK_FALSE - # Trust for "ePKI Root Certification Authority" # Issuer: OU=ePKI Root Certification Authority,O="Chunghwa Telecom Co., Ltd.",C=TW # Serial Number:15:c8:bd:65:47:5c:af:b8:97:00:5e:e4:06:d2:bc:9d @@ -5240,175 +5239,6 @@ CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE -# -# Certificate "Explicitly Distrust DigiNotar Root CA" -# -# Issuer: E=info@diginotar.nl,CN=DigiNotar Root CA,O=DigiNotar,C=NL -# Serial Number:0f:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff -# Subject: E=info@diginotar.nl,CN=DigiNotar Root CA,O=DigiNotar,C=NL -# Not Valid Before: Fri Jul 27 17:19:37 2007 -# Not Valid After : Mon Mar 31 18:19:22 2025 -# Fingerprint (MD5): 0A:A4:D5:CC:BA:B4:FB:A3:59:E3:E6:01:DD:53:D9:4E -# Fingerprint (SHA1): C1:77:CB:4B:E0:B4:26:8E:F5:C7:CF:45:99:22:B9:B0:CE:BA:21:2F -CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE -CKA_TOKEN CK_BBOOL CK_TRUE -CKA_PRIVATE CK_BBOOL CK_FALSE -CKA_MODIFIABLE CK_BBOOL CK_FALSE -CKA_LABEL UTF8 "Explicitly Distrust DigiNotar Root CA" -CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509 -CKA_SUBJECT MULTILINE_OCTAL -\060\137\061\013\060\011\006\003\125\004\006\023\002\116\114\061 -\022\060\020\006\003\125\004\012\023\011\104\151\147\151\116\157 -\164\141\162\061\032\060\030\006\003\125\004\003\023\021\104\151 -\147\151\116\157\164\141\162\040\122\157\157\164\040\103\101\061 -\040\060\036\006\011\052\206\110\206\367\015\001\011\001\026\021 -\151\156\146\157\100\144\151\147\151\156\157\164\141\162\056\156 -\154 -END -CKA_ID UTF8 "0" -CKA_ISSUER MULTILINE_OCTAL -\060\137\061\013\060\011\006\003\125\004\006\023\002\116\114\061 -\022\060\020\006\003\125\004\012\023\011\104\151\147\151\116\157 -\164\141\162\061\032\060\030\006\003\125\004\003\023\021\104\151 -\147\151\116\157\164\141\162\040\122\157\157\164\040\103\101\061 -\040\060\036\006\011\052\206\110\206\367\015\001\011\001\026\021 -\151\156\146\157\100\144\151\147\151\156\157\164\141\162\056\156 -\154 -END -CKA_SERIAL_NUMBER MULTILINE_OCTAL -\002\020\017\377\377\377\377\377\377\377\377\377\377\377\377\377 -\377\377 -END -CKA_VALUE MULTILINE_OCTAL -\060\202\005\212\060\202\003\162\240\003\002\001\002\002\020\017 -\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\060 -\015\006\011\052\206\110\206\367\015\001\001\005\005\000\060\137 -\061\013\060\011\006\003\125\004\006\023\002\116\114\061\022\060 -\020\006\003\125\004\012\023\011\104\151\147\151\116\157\164\141 -\162\061\032\060\030\006\003\125\004\003\023\021\104\151\147\151 -\116\157\164\141\162\040\122\157\157\164\040\103\101\061\040\060 -\036\006\011\052\206\110\206\367\015\001\011\001\026\021\151\156 -\146\157\100\144\151\147\151\156\157\164\141\162\056\156\154\060 -\036\027\015\060\067\060\067\062\067\061\067\061\071\063\067\132 -\027\015\062\065\060\063\063\061\061\070\061\071\062\062\132\060 -\137\061\013\060\011\006\003\125\004\006\023\002\116\114\061\022 -\060\020\006\003\125\004\012\023\011\104\151\147\151\116\157\164 -\141\162\061\032\060\030\006\003\125\004\003\023\021\104\151\147 -\151\116\157\164\141\162\040\122\157\157\164\040\103\101\061\040 -\060\036\006\011\052\206\110\206\367\015\001\011\001\026\021\151 -\156\146\157\100\144\151\147\151\156\157\164\141\162\056\156\154 -\060\202\002\042\060\015\006\011\052\206\110\206\367\015\001\001 -\001\005\000\003\202\002\017\000\060\202\002\012\002\202\002\001 -\000\254\260\130\301\000\275\330\041\010\013\053\232\376\156\126 -\060\005\237\033\167\220\020\101\134\303\015\207\021\167\216\201 -\361\312\174\351\214\152\355\070\164\065\273\332\337\371\273\300 -\011\067\264\226\163\201\175\063\032\230\071\367\223\157\225\177 -\075\271\261\165\207\272\121\110\350\213\160\076\225\004\305\330 -\266\303\026\331\210\260\261\207\035\160\332\206\264\017\024\213 -\172\317\020\321\164\066\242\022\173\167\206\112\171\346\173\337 -\002\021\150\245\116\206\256\064\130\233\044\023\170\126\042\045 -\036\001\213\113\121\161\373\202\314\131\226\151\210\132\150\123 -\305\271\015\002\067\313\113\274\146\112\220\176\052\013\005\007 -\355\026\137\125\220\165\330\106\311\033\203\342\010\276\361\043 -\314\231\035\326\052\017\203\040\025\130\047\202\056\372\342\042 -\302\111\261\271\001\201\152\235\155\235\100\167\150\166\116\041 -\052\155\204\100\205\116\166\231\174\202\363\363\267\002\131\324 -\046\001\033\216\337\255\123\006\321\256\030\335\342\262\072\313 -\327\210\070\216\254\133\051\271\031\323\230\371\030\003\317\110 -\202\206\146\013\033\151\017\311\353\070\210\172\046\032\005\114 -\222\327\044\324\226\362\254\122\055\243\107\325\122\366\077\376 -\316\204\006\160\246\252\076\242\362\266\126\064\030\127\242\344 -\201\155\347\312\360\152\323\307\221\153\002\203\101\174\025\357 -\153\232\144\136\343\320\074\345\261\353\173\135\206\373\313\346 -\167\111\315\243\145\334\367\271\234\270\344\013\137\223\317\314 -\060\032\062\034\316\034\143\225\245\371\352\341\164\213\236\351 -\053\251\060\173\240\030\037\016\030\013\345\133\251\323\321\154 -\036\007\147\217\221\113\251\212\274\322\146\252\223\001\210\262 -\221\372\061\134\325\246\301\122\010\011\315\012\143\242\323\042 -\246\350\241\331\071\006\227\365\156\215\002\220\214\024\173\077 -\200\315\033\234\272\304\130\162\043\257\266\126\237\306\172\102 -\063\051\007\077\202\311\346\037\005\015\315\114\050\066\213\323 -\310\076\034\306\210\357\136\356\211\144\351\035\353\332\211\176 -\062\246\151\321\335\314\210\237\321\320\311\146\041\334\006\147 -\305\224\172\232\155\142\114\175\314\340\144\200\262\236\107\216 -\243\002\003\001\000\001\243\102\060\100\060\017\006\003\125\035 -\023\001\001\377\004\005\060\003\001\001\377\060\016\006\003\125 -\035\017\001\001\377\004\004\003\002\001\006\060\035\006\003\125 -\035\016\004\026\004\024\210\150\277\340\216\065\304\073\070\153 -\142\367\050\073\204\201\310\014\327\115\060\015\006\011\052\206 -\110\206\367\015\001\001\005\005\000\003\202\002\001\000\073\002 -\215\313\074\060\350\156\240\255\362\163\263\137\236\045\023\004 -\005\323\366\343\213\273\013\171\316\123\336\344\226\305\321\257 -\163\274\325\303\320\100\125\174\100\177\315\033\137\011\325\362 -\174\237\150\035\273\135\316\172\071\302\214\326\230\173\305\203 -\125\250\325\175\100\312\340\036\367\211\136\143\135\241\023\302 -\135\212\266\212\174\000\363\043\303\355\205\137\161\166\360\150 -\143\252\105\041\071\110\141\170\066\334\361\103\223\324\045\307 -\362\200\145\341\123\002\165\121\374\172\072\357\067\253\204\050 -\127\014\330\324\324\231\126\154\343\242\376\131\204\264\061\350 -\063\370\144\224\224\121\227\253\071\305\113\355\332\335\200\013 -\157\174\051\015\304\216\212\162\015\347\123\024\262\140\101\075 -\204\221\061\150\075\047\104\333\345\336\364\372\143\105\310\114 -\076\230\365\077\101\272\116\313\067\015\272\146\230\361\335\313 -\237\134\367\124\066\202\153\054\274\023\141\227\102\370\170\273 -\314\310\242\237\312\360\150\275\153\035\262\337\215\157\007\235 -\332\216\147\307\107\036\312\271\277\052\102\221\267\143\123\146 -\361\102\243\341\364\132\115\130\153\265\344\244\063\255\134\160 -\035\334\340\362\353\163\024\221\232\003\301\352\000\145\274\007 -\374\317\022\021\042\054\256\240\275\072\340\242\052\330\131\351 -\051\323\030\065\244\254\021\137\031\265\265\033\377\042\112\134 -\306\172\344\027\357\040\251\247\364\077\255\212\247\232\004\045 -\235\016\312\067\346\120\375\214\102\051\004\232\354\271\317\113 -\162\275\342\010\066\257\043\057\142\345\312\001\323\160\333\174 -\202\043\054\026\061\014\306\066\007\220\172\261\037\147\130\304 -\073\130\131\211\260\214\214\120\263\330\206\313\150\243\304\012 -\347\151\113\040\316\301\036\126\113\225\251\043\150\330\060\330 -\303\353\260\125\121\315\345\375\053\270\365\273\021\237\123\124 -\366\064\031\214\171\011\066\312\141\027\045\027\013\202\230\163 -\014\167\164\303\325\015\307\250\022\114\307\247\124\161\107\056 -\054\032\175\311\343\053\073\110\336\047\204\247\143\066\263\175 -\217\240\144\071\044\015\075\173\207\257\146\134\164\033\113\163 -\262\345\214\360\206\231\270\345\305\337\204\301\267\353 -END - -# Trust for Certificate "Explicitly Distrust DigiNotar Root CA" -# Issuer: E=info@diginotar.nl,CN=DigiNotar Root CA,O=DigiNotar,C=NL -# Serial Number:0f:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff -# Subject: E=info@diginotar.nl,CN=DigiNotar Root CA,O=DigiNotar,C=NL -# Not Valid Before: Fri Jul 27 17:19:37 2007 -# Not Valid After : Mon Mar 31 18:19:22 2025 -# Fingerprint (MD5): 0A:A4:D5:CC:BA:B4:FB:A3:59:E3:E6:01:DD:53:D9:4E -# Fingerprint (SHA1): C1:77:CB:4B:E0:B4:26:8E:F5:C7:CF:45:99:22:B9:B0:CE:BA:21:2F -CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST -CKA_TOKEN CK_BBOOL CK_TRUE -CKA_PRIVATE CK_BBOOL CK_FALSE -CKA_MODIFIABLE CK_BBOOL CK_FALSE -CKA_LABEL UTF8 "Explicitly Distrust DigiNotar Root CA" -CKA_CERT_SHA1_HASH MULTILINE_OCTAL -\301\167\313\113\340\264\046\216\365\307\317\105\231\042\271\260 -\316\272\041\057 -END -CKA_CERT_MD5_HASH MULTILINE_OCTAL -\012\244\325\314\272\264\373\243\131\343\346\001\335\123\331\116 -END -CKA_ISSUER MULTILINE_OCTAL -\060\137\061\013\060\011\006\003\125\004\006\023\002\116\114\061 -\022\060\020\006\003\125\004\012\023\011\104\151\147\151\116\157 -\164\141\162\061\032\060\030\006\003\125\004\003\023\021\104\151 -\147\151\116\157\164\141\162\040\122\157\157\164\040\103\101\061 -\040\060\036\006\011\052\206\110\206\367\015\001\011\001\026\021 -\151\156\146\157\100\144\151\147\151\156\157\164\141\162\056\156 -\154 -END -CKA_SERIAL_NUMBER MULTILINE_OCTAL -\002\020\017\377\377\377\377\377\377\377\377\377\377\377\377\377 -\377\377 -END -CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_NOT_TRUSTED -CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_NOT_TRUSTED -CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_NOT_TRUSTED -CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE - # # Certificate "Security Communication RootCA2" # diff --git a/tools/cpplint.py b/tools/cpplint.py index 622139efb1e2a7..20faef069de5ee 100755 --- a/tools/cpplint.py +++ b/tools/cpplint.py @@ -6489,6 +6489,26 @@ def CheckLocalVectorUsage(filename, lines, error): 'Do not use std::vector>. ' 'Use v8::LocalVector instead.') +def CheckStringValueUsage(filename, lines, error): + """Logs an error if v8's String::Value/Utf8Value are used. + Args: + filename: The name of the current file. + lines: An array of strings, each representing a line of the file. + error: The function to call with any errors found. + """ + if filename.startswith('test/') or filename.startswith('test\\'): + return # Skip test files, where Node.js headers may not be available + + for linenum, line in enumerate(lines): + if Search(r'\bString::Utf8Value\b', line): + error(filename, linenum, 'runtime/v8_string_value', 5, + 'Do not use v8::String::Utf8Value. ' + 'Use node::Utf8Value instead.') + if Search(r'\bString::Value\b', line): + error(filename, linenum, 'runtime/v8_string_value', 5, + 'Do not use v8::String::Value. ' + 'Use node::TwoByteValue instead.') + def ProcessLine(filename, file_extension, clean_lines, line, include_state, function_state, nesting_state, error, extra_check_functions=None): @@ -6660,6 +6680,8 @@ def ProcessFileData(filename, file_extension, lines, error, CheckLocalVectorUsage(filename, lines, error) + CheckStringValueUsage(filename, lines, error) + def ProcessConfigOverrides(filename): """ Loads the configuration files and processes the config overrides. diff --git a/tools/dep_updaters/update-inspector-protocol.sh b/tools/dep_updaters/update-inspector-protocol.sh new file mode 100755 index 00000000000000..8637647b23fcb1 --- /dev/null +++ b/tools/dep_updaters/update-inspector-protocol.sh @@ -0,0 +1,45 @@ +#!/bin/sh +set -e +# Shell script to update inspector_protocol in the source tree to the version same with V8's. + +BASE_DIR=$(cd "$(dirname "$0")/../.." && pwd) +DEPS_DIR="$BASE_DIR/deps" + +# shellcheck disable=SC1091 +. "$BASE_DIR/tools/dep_updaters/utils.sh" + +echo "Making temporary workspace..." + +WORKSPACE=$(mktemp -d 2> /dev/null || mktemp -d -t 'tmp') + +cd "$WORKSPACE" + +git clone https://chromium.googlesource.com/deps/inspector_protocol.git + +INSPECTOR_PROTOCOL_DIR="$WORKSPACE/inspector_protocol" + +echo "Comparing latest upstream with current revision" + +set +e +python "$BASE_DIR/tools/inspector_protocol/roll.py" \ + --ip_src_upstream "$INSPECTOR_PROTOCOL_DIR" \ + --node_src_downstream "$BASE_DIR" +STATUS="$?" +set -e + +if [ "$STATUS" = "0" ]; then + echo "Skipped because inspector_protocol is on the latest version." + exit 0 +fi + +python "$BASE_DIR/tools/inspector_protocol/roll.py" \ + --ip_src_upstream "$INSPECTOR_PROTOCOL_DIR" \ + --node_src_downstream "$BASE_DIR" \ + --force + +NEW_VERSION=$(grep "Revision:" "$DEPS_DIR/inspector_protocol/README.node" | sed -n 's/^Revision: \([[:xdigit:]]\{36\}\).*$/\1/p') + +# Update the version number on maintaining-dependencies.md +# and print the new version as the last line of the script as we need +# to add it to $GITHUB_ENV variable +finalize_version_update "inspector_protocol" "$NEW_VERSION" diff --git a/tools/dep_updaters/update-nixpkgs-pin.sh b/tools/dep_updaters/update-nixpkgs-pin.sh new file mode 100755 index 00000000000000..97d6cde29d8ba7 --- /dev/null +++ b/tools/dep_updaters/update-nixpkgs-pin.sh @@ -0,0 +1,40 @@ +#!/bin/sh +set -ex +# Shell script to update Nixpkgs pin in the source tree to the most recent +# version on the unstable channel. + +BASE_DIR=$(cd "$(dirname "$0")/../.." && pwd) +NIXPKGS_PIN_FILE="$BASE_DIR/tools/nix/pkgs.nix" + +NIXPKGS_REPO=$(grep 'repo =' "$NIXPKGS_PIN_FILE" | awk -F'"' '{ print $2 }') +CURRENT_VERSION_SHA1=$(grep 'rev =' "$NIXPKGS_PIN_FILE" | awk -F'"' '{ print $2 }') +CURRENT_VERSION=$(echo "$CURRENT_VERSION_SHA1" | head -c 7) + +NEW_UPSTREAM_SHA1=$(git ls-remote "$NIXPKGS_REPO.git" nixpkgs-unstable | awk '{print $1}') +NEW_VERSION=$(echo "$NEW_UPSTREAM_SHA1" | head -c 7) + + +# shellcheck disable=SC1091 +. "$BASE_DIR/tools/dep_updaters/utils.sh" + +compare_dependency_version "nixpkgs-unstable" "$NEW_VERSION" "$CURRENT_VERSION" + +CURRENT_TARBALL_HASH=$(grep 'sha256 =' "$NIXPKGS_PIN_FILE" | awk -F'"' '{ print $2 }') +NEW_TARBALL_HASH=$(nix-prefetch-url --unpack "$NIXPKGS_REPO/archive/$NEW_UPSTREAM_SHA1.tar.gz") + +TMP_FILE=$(mktemp) +sed "s/$CURRENT_VERSION_SHA1/$NEW_UPSTREAM_SHA1/;s/$CURRENT_TARBALL_HASH/$NEW_TARBALL_HASH/" "$NIXPKGS_PIN_FILE" > "$TMP_FILE" +mv "$TMP_FILE" "$NIXPKGS_PIN_FILE" + +cat -<.+)") + regex = re.compile(r"version: (?P.+)") for key in [MAVERICKS_PKG_ID, STANDALONE_PKG_ID, FROM_XCODE_PKG_ID]: try: output = GetStdout(["/usr/sbin/pkgutil", "--pkg-info", key]) - return re.search(regex, output).groupdict()["version"] + if m := re.search(regex, output): + return m.groupdict()["version"] except (GypError, OSError): continue regex = re.compile(r"Command Line Tools for Xcode\s+(?P\S+)") try: output = GetStdout(["/usr/sbin/softwareupdate", "--history"]) - return re.search(regex, output).groupdict()["version"] + if m := re.search(regex, output): + return m.groupdict()["version"] except (GypError, OSError): return None diff --git a/tools/gyp/pylib/gyp/xcode_ninja.py b/tools/gyp/pylib/gyp/xcode_ninja.py index 1a97a06c51d9f5..a133fdbe8b4f58 100644 --- a/tools/gyp/pylib/gyp/xcode_ninja.py +++ b/tools/gyp/pylib/gyp/xcode_ninja.py @@ -22,7 +22,7 @@ def _WriteWorkspace(main_gyp, sources_gyp, params): """Create a workspace to wrap main and sources gyp paths.""" - (build_file_root, build_file_ext) = os.path.splitext(main_gyp) + (build_file_root, _build_file_ext) = os.path.splitext(main_gyp) workspace_path = build_file_root + ".xcworkspace" options = params["options"] if options.generator_output: diff --git a/tools/gyp/pylib/gyp/xcodeproj_file.py b/tools/gyp/pylib/gyp/xcodeproj_file.py index 11e2be07372230..cb467470d3044b 100644 --- a/tools/gyp/pylib/gyp/xcodeproj_file.py +++ b/tools/gyp/pylib/gyp/xcodeproj_file.py @@ -487,7 +487,7 @@ def Children(self): children = [] for property, attributes in self._schema.items(): - (is_list, property_type, is_strong) = attributes[0:3] + (is_list, _property_type, is_strong) = attributes[0:3] if is_strong and property in self._properties: if not is_list: children.append(self._properties[property]) @@ -913,7 +913,7 @@ def VerifyHasRequiredProperties(self): # TODO(mark): A stronger verification mechanism is needed. Some # subclasses need to perform validation beyond what the schema can enforce. for property, attributes in self._schema.items(): - (is_list, property_type, is_strong, is_required) = attributes[0:4] + (_is_list, _property_type, _is_strong, is_required) = attributes[0:4] if is_required and property not in self._properties: raise KeyError(self.__class__.__name__ + " requires " + property) @@ -923,7 +923,7 @@ def _SetDefaultsFromSchema(self): defaults = {} for property, attributes in self._schema.items(): - (is_list, property_type, is_strong, is_required) = attributes[0:4] + (_is_list, _property_type, _is_strong, is_required) = attributes[0:4] if ( is_required and len(attributes) >= 5 @@ -1616,7 +1616,7 @@ def __init__(self, properties=None, id=None, parent=None): prop_name = "lastKnownFileType" else: basename = posixpath.basename(self._properties["path"]) - (root, ext) = posixpath.splitext(basename) + (_root, ext) = posixpath.splitext(basename) # Check the map using a lowercase extension. # TODO(mark): Maybe it should try with the original case first and fall # back to lowercase, in case there are any instances where case @@ -2010,7 +2010,7 @@ def Name(self): return "Frameworks" def FileGroup(self, path): - (root, ext) = posixpath.splitext(path) + (_root, ext) = posixpath.splitext(path) if ext != "": ext = ext[1:].lower() if ext == "o": diff --git a/tools/gyp/pyproject.toml b/tools/gyp/pyproject.toml index 3a029c4fc5140c..adc82c3350151f 100644 --- a/tools/gyp/pyproject.toml +++ b/tools/gyp/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "gyp-next" -version = "0.20.4" +version = "0.20.5" authors = [ { name="Node.js contributors", email="ryzokuken@disroot.org" }, ] diff --git a/tools/gyp/tools/graphviz.py b/tools/gyp/tools/graphviz.py index ed1c7ab3cd10b5..65f78011b583c0 100755 --- a/tools/gyp/tools/graphviz.py +++ b/tools/gyp/tools/graphviz.py @@ -47,7 +47,7 @@ def WriteGraph(edges): # Bucket targets by file. files = collections.defaultdict(list) for src, dst in edges.items(): - build_file, target_name, toolset = ParseTarget(src) + build_file, target_name, _toolset = ParseTarget(src) files[build_file].append(src) print("digraph D {") @@ -62,14 +62,14 @@ def WriteGraph(edges): # If there's only one node for this file, simplify # the display by making it a box without an internal node. target = targets[0] - build_file, target_name, toolset = ParseTarget(target) + build_file, target_name, _toolset = ParseTarget(target) print(f' "{target}" [shape=box, label="{filename}\\n{target_name}"]') else: # Group multiple nodes together in a subgraph. print(' subgraph "cluster_%s" {' % filename) print(' label = "%s"' % filename) for target in targets: - build_file, target_name, toolset = ParseTarget(target) + build_file, target_name, _toolset = ParseTarget(target) print(f' "{target}" [label="{target_name}"]') print(" }") diff --git a/tools/inspector_protocol/roll.py b/tools/inspector_protocol/roll.py index 474d163c30a123..92a68d4aa093e4 100644 --- a/tools/inspector_protocol/roll.py +++ b/tools/inspector_protocol/roll.py @@ -38,22 +38,13 @@ def RunCmd(cmd): return stdoutdata.decode('utf-8') -def CheckRepoIsClean(path, suffix): +def CheckRepoIsClean(path): os.chdir(path) # As a side effect this also checks for existence of the dir. # If path isn't a git repo, this will throw and exception. # And if it is a git repo and 'git status' has anything interesting to say, # then it's not clean (uncommitted files etc.) if len(RunCmd(['git', 'status', '--porcelain'])) != 0: raise Exception('%s is not a clean git repo (run git status)' % path) - if not path.endswith(suffix): - raise Exception('%s does not end with /%s' % (path, suffix)) - - -def CheckRepoIsNotAtMainBranch(path): - os.chdir(path) - stdout = RunCmd(['git', 'rev-parse', '--abbrev-ref', 'HEAD']).strip() - if stdout == 'main': - raise Exception('%s is at main branch - refusing to copy there.' % path) def CheckRepoIsInspectorProtocolCheckout(path): @@ -85,6 +76,16 @@ def ReadV8IPRevision(node_src_path): return line[len(REVISION_LINE_PREFIX):] raise Exception('No V8 inspector protocol revision found') + +def ReadNodeIPRevision(node_src_path): + lines = open(os.path.join(node_src_path, 'deps/inspector_protocol/README.node')).readlines() + for line in lines: + line = line.strip() + if line.startswith(REVISION_LINE_PREFIX): + return line[len(REVISION_LINE_PREFIX):] + raise Exception('No Node inspector protocol revision found') + + def CheckoutRevision(path, revision): os.chdir(path) return RunCmd(['git', 'checkout', revision]) @@ -114,16 +115,15 @@ def main(argv): upstream = os.path.normpath(os.path.expanduser(args.ip_src_upstream)) downstream = os.path.normpath(os.path.expanduser( args.node_src_downstream)) - CheckRepoIsClean(upstream, '/src') - CheckRepoIsClean(downstream, '/node') + CheckRepoIsClean(upstream) CheckRepoIsInspectorProtocolCheckout(upstream) - # Check that the destination Git repo isn't at the main branch - it's - # generally a bad idea to check into the main branch, so we catch this - # common pilot error here early. - CheckRepoIsNotAtMainBranch(downstream) # Read V8's inspector_protocol revision v8_ip_revision = ReadV8IPRevision(downstream) + node_ip_revision = ReadNodeIPRevision(downstream) + if v8_ip_revision == node_ip_revision: + print('Node is already at V8\'s inspector_protocol revision %s - nothing to do.' % v8_ip_revision) + sys.exit(0) print('Checking out %s into %s ...' % (upstream, v8_ip_revision)) CheckoutRevision(upstream, v8_ip_revision) diff --git a/tools/msvs/install_tools/install_tools.bat b/tools/msvs/install_tools/install_tools.bat index c3f7a287bfd071..9d759f6398118f 100644 --- a/tools/msvs/install_tools/install_tools.bat +++ b/tools/msvs/install_tools/install_tools.bat @@ -61,6 +61,6 @@ cls -ArgumentList '-NoProfile -InputFormat None -ExecutionPolicy Bypass -Command ^ [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; ^ iex ((New-Object System.Net.WebClient).DownloadString(''https://chocolatey.org/install.ps1'')); ^ - choco upgrade -y python visualstudio2019-workload-vctools; ^ + choco upgrade -y python visualstudio2022-workload-vctools; ^ Read-Host ''Type ENTER to exit'' ' ^ -Verb RunAs diff --git a/tools/msvs/msi/nodemsi/nodemsi.wixproj b/tools/msvs/msi/nodemsi/nodemsi.wixproj index 0e220b9dd51739..4170e53cf738f5 100644 --- a/tools/msvs/msi/nodemsi/nodemsi.wixproj +++ b/tools/msvs/msi/nodemsi/nodemsi.wixproj @@ -1,5 +1,5 @@ - - + + Debug x64 @@ -36,20 +36,24 @@ - - - + + + - + - - move "$(TargetDir)en-us\$(TargetFileName)" "$(TargetPath)" - move "$(TargetDir)en-us\$(TargetPdbFileName)" "$(TargetPdbPath)" - + + + + diff --git a/tools/nix/pkgs.nix b/tools/nix/pkgs.nix new file mode 100644 index 00000000000000..3251e57538a71e --- /dev/null +++ b/tools/nix/pkgs.nix @@ -0,0 +1,10 @@ +arg: +let + repo = "https://github.com/NixOS/nixpkgs"; + rev = "ca77296380960cd497a765102eeb1356eb80fed0"; + nixpkgs = import (builtins.fetchTarball { + url = "${repo}/archive/${rev}.tar.gz"; + sha256 = "1airrw6l87iyny1a3mb29l28na4s4llifprlgpll2na461jd40iy"; + }) arg; +in +nixpkgs diff --git a/tools/test.py b/tools/test.py index 8457bbd939eefc..1b0159ccd9f9d7 100755 --- a/tools/test.py +++ b/tools/test.py @@ -1712,27 +1712,28 @@ def Main(): all_unused = [ ] unclassified_tests = [ ] globally_unused_rules = None - for path in paths: - for arch in options.arch: - for mode in options.mode: - vm = context.GetVm(arch, mode) - if not exists(vm): - print("Can't find shell executable: '%s'" % vm) - continue - archEngineContext = Execute([vm, "-p", "process.arch"], context) - vmArch = archEngineContext.stdout.rstrip() - if archEngineContext.exit_code != 0 or vmArch == "undefined": - print("Can't determine the arch of: '%s'" % vm) - print(archEngineContext.stderr.rstrip()) - continue - env = { - 'mode': mode, - 'system': utils.GuessOS(), - 'arch': vmArch, - 'type': get_env_type(vm, options.type, context), - 'asan': get_asan_state(vm, context), - 'pointer_compression': get_pointer_compression_state(vm, context), - } + + for arch in options.arch: + for mode in options.mode: + vm = context.GetVm(arch, mode) + if not exists(vm): + print("Can't find shell executable: '%s'" % vm) + continue + archEngineContext = Execute([vm, "-p", "process.arch"], context) + vmArch = archEngineContext.stdout.rstrip() + if archEngineContext.exit_code != 0 or vmArch == "undefined": + print("Can't determine the arch of: '%s'" % vm) + print(archEngineContext.stderr.rstrip()) + continue + env = { + 'mode': mode, + 'system': utils.GuessOS(), + 'arch': vmArch, + 'type': get_env_type(vm, options.type, context), + 'asan': get_asan_state(vm, context), + 'pointer_compression': get_pointer_compression_state(vm, context), + } + for path in paths: test_list = root.ListTests([], path, context, arch, mode) unclassified_tests += test_list cases, unused_rules = config.ClassifyTests(test_list, env) diff --git a/tools/v8_gypfiles/features.gypi b/tools/v8_gypfiles/features.gypi index 462af7d2581985..e62b69e9a7175f 100644 --- a/tools/v8_gypfiles/features.gypi +++ b/tools/v8_gypfiles/features.gypi @@ -355,12 +355,12 @@ ['v8_enable_pointer_compression==1', { 'defines': ['V8_COMPRESS_POINTERS'], }], + ['v8_enable_pointer_compression==1 and v8_enable_pointer_compression_shared_cage!=1', { + 'defines': ['V8_COMPRESS_POINTERS_IN_MULTIPLE_CAGES'], + }], ['v8_enable_pointer_compression_shared_cage==1', { 'defines': ['V8_COMPRESS_POINTERS_IN_SHARED_CAGE'], }], - ['v8_enable_pointer_compression==1 and v8_enable_pointer_compression_shared_cage==0', { - 'defines': ['V8_COMPRESS_POINTERS_IN_ISOLATE_CAGE'], - }], ['v8_enable_pointer_compression==1 or v8_enable_31bit_smis_on_64bit_arch==1', { 'defines': ['V8_31BIT_SMIS_ON_64BIT_ARCH',], }], diff --git a/tools/v8_gypfiles/v8.gyp b/tools/v8_gypfiles/v8.gyp index 2d5e78b338b0cb..c3ec7e3ee92bfc 100644 --- a/tools/v8_gypfiles/v8.gyp +++ b/tools/v8_gypfiles/v8.gyp @@ -46,22 +46,31 @@ } }, 'conditions': [ - ['OS=="mac"', { - # Hide symbols that are not explicitly exported with V8_EXPORT. - # TODO(joyeecheung): enable it on other platforms. Currently gcc times out - # or run out of memory with -fvisibility=hidden on some machines in the CI. - 'xcode_settings': { - 'GCC_SYMBOLS_PRIVATE_EXTERN': 'YES', # -fvisibility=hidden - }, + # Build with -fvisibility=hidden and -fvisibility-inlines-hidden to avoid + # including unnecessary internal symbols, which may lead to run-time fixups. + # This is not done on AIX where symbols are exported by tools/create_expfile.sh + # see https://github.com/nodejs/node/pull/56290#issuecomment-2582703109 + ['OS!="aix" and OS!="os400"', { 'defines': [ 'BUILDING_V8_SHARED', # Make V8_EXPORT visible. - ], + ] }], ['node_shared=="true"', { 'defines': [ 'V8_TLS_USED_IN_LIBRARY', # Enable V8_TLS_LIBRARY_MODE. ], }], + ['OS=="mac"', { + 'xcode_settings': { + 'GCC_SYMBOLS_PRIVATE_EXTERN': 'YES', # -fvisibility=hidden + 'GCC_INLINES_ARE_PRIVATE_EXTERN': 'YES' # -fvisibility-inlines-hidden + }, + }, '(OS!="aix" and OS!="os400") and (OS!="win" or clang==1)', { + 'cflags': [ + '-fvisibility=hidden', + '-fvisibility-inlines-hidden' + ], + }], # MSVC hides the non-public symbols by default so no need to configure it. ], }, 'targets': [ diff --git a/typings/internalBinding/config.d.ts b/typings/internalBinding/config.d.ts index 68c1002f413652..9fa5713fb7c89c 100644 --- a/typings/internalBinding/config.d.ts +++ b/typings/internalBinding/config.d.ts @@ -10,6 +10,5 @@ export interface ConfigBinding { hasInspector: boolean; noBrowserGlobals: boolean; bits: number; - hasDtrace: boolean; getDefaultLocale(): string; } diff --git a/typings/internalBinding/http_parser.d.ts b/typings/internalBinding/http_parser.d.ts index 124bdd5af2f152..162081dc2ccb86 100644 --- a/typings/internalBinding/http_parser.d.ts +++ b/typings/internalBinding/http_parser.d.ts @@ -27,10 +27,18 @@ declare namespace InternalHttpParserBinding { static kLenientHeaders: number; static kLenientChunkedLength: number; static kLenientKeepAlive: number; + static kLenientTransferEncoding: number; + static kLenientVersion: number; + static kLenientDataAfterClose: number; + static kLenientOptionalLFAfterCR: number; + static kLenientOptionalCRLFAfterChunk: number; + static kLenientOptionalCRBeforeLF: number; + static kLenientSpacesAfterChunkSize: number; static kLenientAll: number; close(): void; free(): void; + remove(): void; execute(buffer: Buffer): Error | Buffer; finish(): Error | Buffer; initialize( diff --git a/typings/internalBinding/worker.d.ts b/typings/internalBinding/worker.d.ts index e98cd62b21f868..e168ff8abb1711 100644 --- a/typings/internalBinding/worker.d.ts +++ b/typings/internalBinding/worker.d.ts @@ -11,6 +11,7 @@ declare namespace InternalWorkerBinding { ); startThread(): void; stopThread(): void; + hasRef(): boolean; ref(): void; unref(): void; getResourceLimits(): Float64Array; @@ -38,7 +39,9 @@ export interface WorkerBinding { Worker: typeof InternalWorkerBinding.Worker; getEnvMessagePort(): InternalMessagingBinding.MessagePort; threadId: number; + threadName: string; isMainThread: boolean; + isInternalThread: boolean; ownsProcessState: boolean; resourceLimits?: Float64Array; kMaxYoungGenerationSizeMb: number; diff --git a/vcbuild.bat b/vcbuild.bat index ece8f222ad8f68..672479614c54d0 100644 --- a/vcbuild.bat +++ b/vcbuild.bat @@ -353,7 +353,7 @@ del .gyp_configure_stamp 2> NUL @rem Generate the VS project. echo configure %configure_flags% echo %configure_flags%> .used_configure_flags -python configure %configure_flags% +call python configure %configure_flags% if errorlevel 1 goto create-msvs-files-failed if not exist node.sln goto create-msvs-files-failed set project_generated=1